Spring深入學(xué)習(xí)_IOC淺析上

Spring Framework

  • 發(fā)布于2002年10月1日
  • 強大的基于 JavaBeans 的采用控制反轉(zhuǎn)(Inversion of Control,IoC)原則的配置管理,使得應(yīng)用程序的組建更加簡易快捷东囚。

  • 一個可用于 Java EE 等運行環(huán)境的核心 Bean工廠缩焦。

  • 數(shù)據(jù)庫事務(wù)的一般化抽象層涝涤,允許聲明式(Declarative)事務(wù)管理器备恤,簡化事務(wù)的劃分使之與底層無關(guān)蚓耽。

  • 內(nèi)建的針對 JTA 和單個 JDBC 數(shù)據(jù)源的一般化策略栅葡,使Spring的事務(wù)支持不要求 Java EE 環(huán)境,這與一般的 JTA 或者 EJB CMT 相反沧踏。

  • JDBC 抽象層提供了有針對性的異常等級(不再從 SQL 異常中提取原始代碼)歌逢,簡化了錯誤處理,大大減少了程序員的編碼量翘狱。再次利用 JDBC 時秘案,你無需再寫出另一個'終止'(finally)模塊。并且面向 JDBC 的異常與 Spring 通用數(shù)據(jù)訪問對象(Data Access Object)異常等級相一致潦匈。

  • 以資源容器阱高,DAO 實現(xiàn)和事務(wù)策略等形式與 HibernateJDOMyBatis 茬缩、SQL Maps 集成赤惊。利用控制反轉(zhuǎn)機制全面解決了許多典型的 Hibernate 集成問題。所有這些全部遵從 Spring 通用事務(wù)處理和通用數(shù)據(jù)訪問對象異常等級規(guī)范凰锡。

  • 靈活的基于核心 Spring 功能的 MVC 網(wǎng)頁應(yīng)用程序框架未舟。開發(fā)者通過策略接口將擁有對該框架的高度控制,因而該框架將適應(yīng)于多種呈現(xiàn)(View)技術(shù)掂为,例如 JSP裕膀、FreeMarkerVelocity勇哗、Thymeleaf 等昼扛。值得注意的是,Spring 中間層可以輕易地結(jié)合于任何基于 MVC 框架的網(wǎng)頁層智绸,例如 Struts野揪、WebWorkTapestry

  • 提供諸如事務(wù)管理等服務(wù)的AOP框架瞧栗。

在設(shè)計應(yīng)用程序 Model 時斯稳,MVC模式(例如 Struts)通常難于給出一個簡潔明了的框架結(jié)構(gòu)。Spring 卻具有能夠讓這部分工作變得簡單的能力迹恐。程序開發(fā)員們可以使用Spring的JDBC抽象層重新設(shè)計那些復(fù)雜的框架結(jié)構(gòu)挣惰。

上述是維基百科給出的Spring核心功能模塊定義,我們可以看出Spring做了什么有哪些用處。Spring是一個BeanFactory,負責(zé)加載Bean和管理Bean的生命周期與依賴關(guān)系這也就是IOC控制反轉(zhuǎn),它還支持AOP以便支持切面編程,Spring MVC給出了一個簡單的MVC模式范例很方便的支持日常用到的貧血模型,以及Spring Boot可以支持簡化配置殴边。

BeanFactory

既然知道了Spring中IOC的核心組件時BeanFactory,那么我們來了解一下什么是BeanFactory憎茂。見詞曉意一個組件工廠,用來生產(chǎn)組件和獲得組件。

public interface BeanFactory {
  //用&MyFactory獲得工廠,&為表示工廠的前綴
  String FACTORY_BEAN_PREFIX = "&";
  //通過名字或別名獲取Bean
  Object getBean(String name) throws BeansException;
  //獲取時要匹配類型requiredType需要是超類或者繼承的接口
  <T> T getBean(String name, Class<T> requiredType) throws BeansException;
  //args使用顯示參數(shù)創(chuàng)建Bean時需要用到的參數(shù)
  Object getBean(String name, Object... args) throws BeansException;
  //通過類型獲取Bean
  <T> T getBean(Class<T> requiredType) throws BeansException;
  //類型和參數(shù)
  <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
  //返回一個Bean提供程序允許檢測可用性和唯一性支持懶加載,就是一個ObjectFactory
  <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
  //根據(jù)name查找是否含有Bean定義和單例Bean,如果是分層的工廠則查找超類
  boolean containsBean(String name);
  //是否是單例的們也會遞歸查找 PS:單例是每次調(diào)用都可以返回相同實例
  boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
  //Spring支持的原型類型也就是每次調(diào)用都返回一個新的實例
  boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
  //檢查是否可以返回一個給定意義的對象,ResolvableType中定義了類型
  boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
  //Class版本的type
  boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
  //拿到類型,對于FactoryBean返回工廠生產(chǎn)的Bean實例類型,默認會初始化工廠
  Class<?> getType(String name) throws NoSuchBeanDefinitionException;
  //allow詢問要不要初始化工廠
  Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
  //返回別名,原始名稱為數(shù)組中的第一個元素
  String[] getAliases(String name);

上述就是BeanFactory全部方法,先是一些重載的getBean各種類型如用name獲取,或者用requiredType獲取再是獲取ObjectProvider再者是檢測有沒有這個Bean,Bean的類型與生成規(guī)則,最后是拿到這個Bean的Type與別名锤岸∈#可以看出這個工廠是Spring最核心的組件。
什么是耦合呢?為什么使用new會造成耦合?

  //一句簡單的泛型,接口持有子類引用
  ProfileService profileService = new ProfileServiceImpl();

上述代碼為題在哪里呢?其實也沒啥問題,如果是寫個小項目完全沒有任何問題有時候我們需要替換Impl時直接Ctrl+F定位Impl然后正則替換!完結(jié)撒花,Spring結(jié)束!
開個玩笑,假設(shè)項目中有很多中這樣的impl很常見的,我們在替換時需要替換每一個地方的,但是有些地方有沒有什么可以快速替換的方式呢?有的
(XXXImpl)BeanFactroy.getBean(XXX);
好處時什么呢?我們可以直接在Bean工廠中替換Impl實現(xiàn),如我們采用注解的方式實現(xiàn)一個接口,在實現(xiàn)類Impl上標(biāo)明采用這個實現(xiàn)類,如果下一次想換一個那么把這個注解移到另一個類就可以,這樣子對代碼中侵入性修改就可以降低到最小是偷。

ApplicationContext 應(yīng)用上下文

ApplicationContext

從上圖中,我們可以很明顯看出Application繼承自BeanFactory,Listable是可以一次獲取多個Bean,BeanFactory的方法中都是獲取單個,Hierarchical,可以有繼承關(guān)系的BeanFactory拳氢。應(yīng)用程序中常見的兩個Appcontext有ClassPathXmlApplicationContext,AnnotationConfigApplicationContext


appcontext繼承關(guān)系

上述最要中的觀察點已經(jīng)標(biāo)識,程序中兩種最重要的上下文可以采用注解和Xml兩種形式,ApplicationContext啟動過程中會負責(zé)創(chuàng)建Bean與Bean的依賴注入。

分析ApplicationContext的啟動過程

我們從ClassPathXmlApplicationContext的源碼中分析context的啟動過程,進入源碼中發(fā)現(xiàn)只有200多行美滋滋蛋铆。

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  @Nullable
  private Resource[] configResources;
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  public ClassPathXmlApplicationContext(
        String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
        throws BeansException {
    super(parent);
    //處理配置文件數(shù)組
    setConfigLocations(configLocations);
    if (refresh) {
        //核心方法刷新
        refresh();
    }
  }

1.setConfigLocations

setConfigLocations是超類AbstractRefreshableConfigApplicationContext中的方法用于解析配置文件路徑,處理成配置文件數(shù)組馋评。也就是一個String[]configLocations,先判斷下非空再對Path進行trim去除空白符再resolvePath進行解析。

2.refresh()

這個方法也是由超類實現(xiàn)的值得一提的是為什么叫refresh而不是init那因為可以重新調(diào)用這個方法來進行銷毀重建,具體代碼如下

    public void refresh() throws BeansException, IllegalStateException {
        // 加鎖以防止refresh沒結(jié)束又進行一個刷新
        synchronized (this.startupShutdownMonitor) {
            // 準(zhǔn)備上下文用于刷新
            prepareRefresh();

            // 刷新內(nèi)部Bean工廠,解析配置文件生成Bean定義注冊到BeanFactory
            // 沒有進行初始化Bean
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 設(shè)置BeanFactory的類加載器 添加幾個BeanPostProcessor手動注冊一些單例Bean
            prepareBeanFactory(beanFactory);

            try {
                // 擴展點,如果類實現(xiàn)了這個這個方法可以再容器初始化后做些什么
                postProcessBeanFactory(beanFactory);

                // 調(diào)用processors
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注冊BeanPostProcessors
                // 如果Bean實現(xiàn)了接口的兩個方法postProcessBeforeInitialization和postProcessAfterInitialization
                //那么在Bean初始化的過程之前和之后會進行調(diào)用
                registerBeanPostProcessors(beanFactory);

                // 初始化當(dāng)前context的信息源
                initMessageSource();

                // 初始化時間廣播器
                initApplicationEventMulticaster();

                // 初始化一些特殊的Bean
                onRefresh();

                // 注冊事件監(jiān)聽器
                registerListeners();

                // 初始化所有單例懶加載的除外
                finishBeanFactoryInitialization(beanFactory);

                // 最后一步廣播初始化完成事件
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // 發(fā)生異常則銷毀創(chuàng)建的單例
                destroyBeans();

                // 重置active標(biāo)志位為false
                cancelRefresh(ex);

                // 異常拋出去
                throw ex;
            }

            finally {
                //設(shè)置內(nèi)省緩存,可能不在需要單例的元數(shù)據(jù)
                resetCommonCaches();
            }
        }
    }

這個refresh內(nèi)容蠻多的,具體概括一下就是刷新一下BeanFactory加載新的Bean定義再初始化一些特殊Bean與注冊事件監(jiān)聽器初始化單例,消除單例Bean的元數(shù)據(jù),下面我們簡單分析一下每個調(diào)用語句刺啦。

創(chuàng)建BeanFactory之前的準(zhǔn)備工作

    protected void prepareRefresh() {
        // 設(shè)置啟動時間與標(biāo)志位
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isDebugEnabled()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Refreshing " + this);
            }
            else {
                logger.debug("Refreshing " + getDisplayName());
            }
        }

        // 初始化占位符屬性源
        initPropertySources();

        // 校驗標(biāo)記文件是否可以解析
        getEnvironment().validateRequiredProperties();
        // 存儲earlyApplicationListeners到applicationListeners,并新建一個
        // Store pre-refresh ApplicationListeners...
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

創(chuàng)建Bean容器,加載Bean并注冊

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //刷新BeanFactroy如果有舊的就關(guān)閉再創(chuàng)建個新的
        //加載Bean定義與注冊Bean
        refreshBeanFactory();
        //返回新的beanFactory
        return getBeanFactory();
    }
    //在AbstractRefreshableApplicationContext有這個方法的實現(xiàn)
    protected final void refreshBeanFactory() throws BeansException {
        //如果加載過BeanFactory就銷毀掉所有Bean并關(guān)閉BeanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //創(chuàng)建一個DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //設(shè)置序列化ID
            beanFactory.setSerializationId(getId());
            // 定制一下BeanFactory允許Bean的覆蓋與循環(huán)引用
            customizeBeanFactory(beanFactory);
            // 加載Bean定義
            loadBeanDefinitions(beanFactory);
            //設(shè)置beanFactory
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

方法很簡單主要是為什么BeanFactory要使用DefaultListableBeanFactory,這個類繼承了所有實現(xiàn),被設(shè)計成一個默認的BeanFactory


DefaultListableBeanFactory

loadBeanDefinitions加載BeanDefinition

什么是BeanDefinition?Bean的定義保存了Bean的元信息指向哪個類,是否是單例要不要懶加載Bean依賴于哪些Bean
BeanDefinition定義

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    //默認提供兩種類型單例與原型
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


    //Bean的角色
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;


    // Modifiable attributes

    //設(shè)置父Bean也就是繼承父Bean的配置信息
    void setParentName(@Nullable String parentName);

    // 獲取父Bean
    @Nullable
    String getParentName();

    // 設(shè)置Bean的類名
    void setBeanClassName(@Nullable String beanClassName);

    // 獲取Bean的類名
    @Nullable
    String getBeanClassName();

    // 設(shè)置作用域
    void setScope(@Nullable String scope);

    // 獲取作用域
    @Nullable
    String getScope();

    // 設(shè)置懶加載
    void setLazyInit(boolean lazyInit);

    
    boolean isLazyInit();

    // 設(shè)置依賴的Bean 重點BeanFactory可以保證這些依賴的Bean可以在他前面初始化
    void setDependsOn(@Nullable String... dependsOn);

    // 返回依賴Bean名稱
    @Nullable
    String[] getDependsOn();

    // 設(shè)置是否可以注入到其他Bean只影響類型注入
    // 顯示的名稱注入依然有效
    void setAutowireCandidate(boolean autowireCandidate);

    boolean isAutowireCandidate();

    // 設(shè)置Primary 同一個接口多個實現(xiàn)不指定名稱會選擇主要那個
    void setPrimary(boolean primary);

    boolean isPrimary();

    // 如果是工廠模式生產(chǎn)的就設(shè)置一下工廠
    void setFactoryBeanName(@Nullable String factoryBeanName);

    @Nullable
    String getFactoryBeanName();

    // 用哪個工廠方法
    void setFactoryMethodName(@Nullable String factoryMethodName);

    @Nullable
    String getFactoryMethodName();

    // 構(gòu)造器參數(shù)值
    ConstructorArgumentValues getConstructorArgumentValues();

    // 構(gòu)造器是否是有參的
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    // Bean中的屬性值
    MutablePropertyValues getPropertyValues();

    
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    // 設(shè)置初始化方法名稱
    void setInitMethodName(@Nullable String initMethodName);

    
    @Nullable
    String getInitMethodName();

    // 設(shè)置銷毀方法名稱
    void setDestroyMethodName(@Nullable String destroyMethodName);

    
    @Nullable
    String getDestroyMethodName();

    // 設(shè)置角色
    void setRole(int role);

    
    int getRole();

    // 提供人可以讀的描述
    void setDescription(@Nullable String description);

    
    @Nullable
    String getDescription();


    // 只讀屬性

    // 返回可解析類型
    ResolvableType getResolvableType();

    
    boolean isSingleton();

    
    boolean isPrototype();

    boolean isAbstract();

    
    @Nullable
    String getResourceDescription();


    @Nullable
    BeanDefinition getOriginatingBeanDefinition();

}

定義了Bean的作用域,構(gòu)造器,Bean生成規(guī)則,初始化和銷毀方法
回到refreshBeanFactory() 中剩下還有兩個重要方法

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        //是否允許Bean定義覆蓋
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        //是否允許循環(huán)依賴
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

Spring的默認值是同一配置文件不允許覆蓋,不同則可以留特。而默認情況下Spring允許循環(huán)依賴,構(gòu)造方法的循環(huán)依賴不允許

加載BeanDefinition

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws       BeansException, IOException {
        // 實例化一個XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // 允許子類提供BeanDefinitionReader的自定義初始化
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions 很重要的方法用是初始化的Reader開始加載Bean定義

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        // 使用其他重載的方法以進行加載
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        // 簡單的遍歷加載然后返回加載個數(shù)
        for (Resource resource : resources) {
            count += loadBeanDefinitions(resource);
        }
        return count;
    }

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        // 用ThreadLocal來存放配置文件資源
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }

        try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //核心加載方法
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //XML轉(zhuǎn)DOM
            Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
    // 具體的parse解析方法有很多種實現(xiàn),這里不展開了如果學(xué)了編譯原理應(yīng)該無壓力
    public void parse(XMLInputSource source) throws XNIException, IOException {

        if (fParseInProgress) {
            // REVISIT - need to add new error message
            throw new XNIException("FWK005 parse may not be called while parsing.");
        }
        fParseInProgress = true;

        try {
            setInputSource(source);
            parse(true);
        } catch (XNIException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (IOException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (RuntimeException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (Exception ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw new XNIException(ex);
        } finally {
            fParseInProgress = false;
            // close all streams opened by xerces
            this.cleanup();
        }

    }
    public boolean parse(boolean complete) throws XNIException, IOException {
        //
        // reset and configure pipeline and set InputSource.
        if (fInputSource != null) {
            try {
                fValidationManager.reset();
                fVersionDetector.reset(this);
                fConfigUpdated = true;
                resetSymbolTable();
                resetCommon();

                short version = fVersionDetector.determineDocVersion(fInputSource);
                if (version == Constants.XML_VERSION_1_1) {
                    initXML11Components();
                    configureXML11Pipeline();
                    resetXML11();
                } else {
                    configurePipeline();
                    reset();
                }

                // mark configuration as fixed
                fConfigUpdated = false;

                // resets and sets the pipeline.
                fVersionDetector.startDocumentParsing((XMLEntityHandler) fCurrentScanner, version);
                fInputSource = null;
            } catch (IOException | RuntimeException ex) {
                if (PRINT_EXCEPTION_STACK_TRACE)
                    ex.printStackTrace();
                throw ex;
            } catch (Exception ex) {
                if (PRINT_EXCEPTION_STACK_TRACE)
                    ex.printStackTrace();
                throw new XNIException(ex);
            }
        }

        try {
            return fCurrentScanner.scanDocument(complete);
        } catch (IOException | RuntimeException ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw ex;
        } catch (Exception ex) {
            if (PRINT_EXCEPTION_STACK_TRACE)
                ex.printStackTrace();
            throw new XNIException(ex);
        }

    }

經(jīng)過如上的漫長方法,可以把配置文件轉(zhuǎn)換成DOM樹上面僅僅是轉(zhuǎn)換成DOM樹下面介紹下解析DOM樹

DefaultBeanDefinitionDocumentReader

    protected void doRegisterBeanDefinitions(Element root) {
        // 方法可以遞歸調(diào)用 在parseDefaultElement中調(diào)用了doRegisterBeanDefinitions
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        //判斷一下是否是根節(jié)點
        //URL里是否含有BEANS_NAMESPACE_URI
        if (this.delegate.isDefaultNamespace(root)) {
            // 讀取beans的profile 
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                //按照 ,;對profileSpec差分
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        //鉤子函數(shù)
        preProcessXml(root);

        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
    // 解析文檔中 import alias bean等元素
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        //解析Default
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //解析定制的
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
    public boolean isDefaultNamespace(@Nullable String namespaceUri) {
        return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
    }

    /**
     * Determine whether the given node indicates the default namespace.
     */
    public boolean isDefaultNamespace(Node node) {
        return isDefaultNamespace(getNamespaceURI(node));
    }
    public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

可以看出默認的Namespace也就是要測試namespaceUri是否是BEANS_NAMESPACE_URI

    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName" >
    

上述為一段典型的xml定義,上面有就使用到BEANS_NAMESPACE_URI,也就是解析XML中的各個節(jié)點,常見的有import,bean,aop,context,mvc,區(qū)別在于BEANS_NAMESPACE_URI中定義的是默認的其他的是定制的。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //解析<import/>
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        //解析<alias/>
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        //解析<bean/>
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        //解析<nested/>
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

對importBeanDefinitionResource進行一個淺析,讀取一下元素再查看一下import里面有沒有resuouce然后對引入資源進行加載

    <import resource = "xxxxx.xml">
    public static final String RESOURCE_ATTRIBUTE = "resource";
    //簡述
    protected void importBeanDefinitionResource(Element ele) {
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }
        -----省略
    }

總結(jié)

對今天的BeanFactory解析進行一個總結(jié),我們深入探討了BeanFactory的模型與ApplicationContext,知道了上下文的refresh方法用于生成Bean定義與銷毀重加載BeanFactory,對BeanDefinition進行了深入了解和解析XML文件,最后分析了一下XML文件生成的DOM樹如何解析并對一些簡單的如<import resuource = "">進行了淺顯的解析已推出XML文件的解析大致流程玛瘸。

今天時間有限先對Spring框架做一個大致了解,知道什么是BeanFactory和ApplicationContext即可蜕青。朋友們走過路過不要忘記點贊,你的點贊是我更新的動力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捧韵,一起剝皮案震驚了整個濱河市市咆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌再来,老刑警劉巖蒙兰,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芒篷,居然都是意外死亡搜变,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門针炉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挠他,“玉大人,你說我怎么就攤上這事篡帕≈城郑” “怎么了贸呢?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拢军。 經(jīng)常有香客問我楞陷,道長,這世上最難降的妖魔是什么茉唉? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任固蛾,我火速辦了婚禮,結(jié)果婚禮上度陆,老公的妹妹穿的比我還像新娘艾凯。我一直安慰自己,他們只是感情好懂傀,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布趾诗。 她就那樣靜靜地躺著,像睡著了一般鸿竖。 火紅的嫁衣襯著肌膚如雪沧竟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天缚忧,我揣著相機與錄音悟泵,去河邊找鬼警没。 笑死谦去,一個胖子當(dāng)著我的面吹牛胳搞,可吹牛的內(nèi)容都是我干的茵休。 我是一名探鬼主播崭添,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼臊岸,長吁一口氣:“原來是場噩夢啊……” “哼梗脾!你這毒婦竟也來了尽楔?” 一聲冷哼從身側(cè)響起持钉,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衡招,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后每强,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體始腾,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年空执,在試婚紗的時候發(fā)現(xiàn)自己被綠了浪箭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辨绊,死狀恐怖奶栖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤宣鄙,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布袍镀,位于F島的核電站,受9級特大地震影響冻晤,放射性物質(zhì)發(fā)生泄漏流椒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一明也、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惯裕,春花似錦温数、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至握玛,卻和暖如春够傍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挠铲。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工冕屯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拂苹。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓安聘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瓢棒。 傳聞我的和親對象是個殘疾皇子浴韭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354