1. 程式人生 > >Spring中IOC容器的初始化過程

Spring中IOC容器的初始化過程

`原文連結
Spring IOC容器初始化過程分為Resource定位,載入解析,註冊。IOC容器初始化過程中不包含Bean的依賴注入。Bean的依賴注入一般會發生在第一次通過getBean向容器索取Bean的時候。

ClassPathXmlApplicationContext初始化過程

實際的構造方法:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
        throws BeansException {
    //super方法為容器設定好Bean資源載入器
//該方法最終會呼叫到AbstractApplicationContext的無參構造方法 //這裡會預設設定解析路徑的模式為Ant-style super(parent); //設定Bean定義資原始檔的路徑 setConfigLocations(configLocations); if (refresh) { //呼叫容器的refresh,載入BeanDefinition的入口 refresh(); } }

refresh()方法:

public void refresh() throws BeansException, IllegalStateException {
    synchronized
(this.startupShutdownMonitor) { // 呼叫容器準備重新整理的方法,此方法中會獲取容器的當前時間,給容器設定同步標識 //初始化前的準備工作,例如對系統屬性或環境變數進行準備以及驗證。 prepareRefresh(); //通知子類啟動refreshBeanFactory的呼叫 //初始化BeanFactory,並進行XML檔案讀取 //ClassPathXmlApplicationContext包含著BeanFactory所提供的一切特徵 //這一步會複用BeanFactory中的配置檔案讀取解析以及其他功能
//這一步之後ClassPathXmlApplicationContext已經包含了BeanFactory所提供的功能,可以進行Bean的提取等基礎操作了。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //準備當前上下文用的BeanFactory,為BeanFactory配置容器特性,例如類載入器,事件處理器等各種功能填充。 //對BeanFactory各種功能的填充,比如@Qualifier和@Autowired註解就是在這一步增加的支援 prepareBeanFactory(beanFactory); try { //為子類設定BeanFactory的後置處理器 //子類覆蓋方法做額外的處理。 postProcessBeanFactory(beanFactory); //呼叫BeanFactoryPostProcessor,啟用各種BeanFactory處理器 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //註冊攔截Bean建立的Bean處理器,這裡只是註冊,真正呼叫實在getBean的時候。 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //為上下文初始化Message源,國際化處理 initMessageSource(); // Initialize event multicaster for this context. //初始化應用訊息廣播器,並放入applicationEventMulticaster bean中 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. 留給子類來初始化其他的Bean onRefresh(); // Check for listener beans and register them. 在所有註冊的bean中查詢Listener bean,註冊到訊息廣播器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //初始化剩下的單例項,非惰性的 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. 完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshEvent通知別人 finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }

重點看下ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();這句,告訴子類啟動refreshBeanFactory方法以及通過getBeanFactory獲得BeanFactory:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

refreshBeanFactory方法在AbstractRefreshableApplicationContext實現:

protected final void refreshBeanFactory() throws BeansException {
    //如果已經存在BeanFactory,銷燬並關閉
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        //建立IOC容器,建立了DefaultListableBeanFactory容器,給ApplicationContext使用
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        //啟動對BeanDefinitions的載入
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

對BeanDefinition的載入,loadBeanDefinitions方法:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    //建立bean讀取器,容器使用該讀取器去讀取Bean定義資源
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    //配置bean讀取器
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    //當Bean讀取器讀取Bean定義的xml資原始檔時,啟用xml的校驗機制
    initBeanDefinitionReader(beanDefinitionReader);
    //通過beanDefinitionReader載入BeanDefinitions
    loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions(beanDefinitionReader)方法:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //獲得Bean配置檔案的資源位置
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        //最終還是轉換成Resource的形式去載入資源
        reader.loadBeanDefinitions(configLocations);
    }
}

在AbstractBeanDefinitionReader中的loadBeanDefinitions(configResources)方法:

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    for (Resource resource : resources) {
        //此處loadBeanDefinitions並沒有實現,具體實現在各個子類中
        //比如XmlBeanDefinitionReader中
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

XmlBeanDefinitionReader中的loadBeanDefinitions方法:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    try {
        //得到xml檔案的InputStream
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            //得到InputSource
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //doLoadBeanDefinitions是從xml檔案中載入BeanDefinitions
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            inputStream.close();
        }
    }
    catch (IOException ex) {......}
    finally {......}
}

xml配置檔案的讀取以及轉化為Bean物件的過程

當Spring定位到xml之後,將xml轉換為檔案流的形式,作為InputSource和Resource物件傳遞給文件解析器進行解析,文件解析的開始是XmlDefinitionReader的doLoadBeanDefinitions方法。

//inputSource是SAX的InputSource
//resource物件是對xml檔案描述的一個物件
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        //xml的解析模式
        int validationMode = getValidationModeForResource(resource);
        //將inputResource解析為Document物件
        Document doc = this.documentLoader.loadDocument(
                inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
        //Document傳遞給registerBeanDefinitions方法
        //此方法才是真正把Document物件解析為BeanDefinition物件的具體實現
        return registerBeanDefinitions(doc, resource);
    }
}

registerBeanDefinitions方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
    if (this.parserClass != null) {
        XmlBeanDefinitionParser parser =
                (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
        return parser.registerBeanDefinitions(this, doc, resource);
    }
    //先例項化一個BeanDefinitionDocumentReader,這個物件是通過BeanUtils.instantiateClass方法例項化出來的
    //實際上BeanUtils.instantiateClass中是封裝了Java的反射的一些方法,通過基本的Java反射來構造例項。
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

    //記錄下註冊之前BeanDefinition中物件的個數。
    int countBefore = getRegistry().getBeanDefinitionCount();
    //開始解析Document
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

DefaultBeanDefinitionDocumentReader類中的registerBeanDefinitions方法:

//在這裡解析Document
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);
}

doRegisterBeanDefinitions方法:

protected void doRegisterBeanDefinitions(Element root) {
    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    if (StringUtils.hasText(profileSpec)) {
        Assert.state(this.environment != null, "environment property must not be null");
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        if (!this.environment.acceptsProfiles(specifiedProfiles)) {
            return;
        }
    }

    //BeanDefinitionParserDelegate物件描述了Spring中bean節點中定義的所有屬性和子節點
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createHelper(readerContext, root, parent);

    //xml解析的預處理,可以自己定義一些節點屬性等,此方法Spring預設實現為空
    preProcessXml(root);
    //把Document物件解析為BeanDefinition物件
    parseBeanDefinitions(root, this.delegate);
    //xml解析的後處理,可以在解析完xml之後,實現自己的邏輯。Spring預設實現為空。
    postProcessXml(root);

    this.delegate = parent;
}

parseBeanDefinitions方法:

//解析在root級別的元素,比如import,alias,bean
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    //校驗是不是Spring的預設名稱空間。
    //預設名稱空間是http://www.springframework.org/schema/beans
    //如果是Spring的預設名稱空間,就按照預設名稱空間來解析,否則就按照自定義標籤來解析
    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)) {
                    //解析Spring預設的標籤
                    parseDefaultElement(ele, delegate);
                }
                else {
                    //解析自定義標籤
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        //解析自定義標籤
        delegate.parseCustomElement(root);
    }
}

解析預設的標籤,parseDefaultElement方法:

//此方法會依次解析文件中的import,alias,bean,beans標籤
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

import標籤的解析,importBeanDefinitionResource方法:

protected void importBeanDefinitionResource(Element ele) {
    //獲取import標籤中的resource屬性,此屬性表示資源地址
    //resource屬性不可為空
    String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
    if (!StringUtils.hasText(location)) {
        getReaderContext().error("Resource location must not be empty", ele);
        return;
    }

    // 解析resource中的佔位符為真正的路徑,比如"${user.dir}"
    location = environment.resolveRequiredPlaceholders(location);

    Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

    // 解析路徑,判斷是相對路徑還是絕對路徑
    boolean absoluteLocation = false;
    try {
        absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
    }
    catch (URISyntaxException ex) {...}
    //絕對路徑
    if (absoluteLocation) {
        try {
            //遞迴呼叫Bean的解析過程
            int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
        }
    }
    else {//相對路徑,計算出絕對路徑,進行遞迴呼叫解析過程
        try {
            int importCount;
            Resource relativeResource = getReaderContext().getResource().createRelative(location);
            if (relativeResource.exists()) {
                importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                actualResources.add(relativeResource);
            }
            else {
                String baseLocation = getReaderContext().getResource().getURL().toString();
                importCount = getReaderContext().getReader().loadBeanDefinitions(
                        StringUtils.applyRelativePath(baseLocation, location), actualResources);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
            }
        }
        catch (IOException ex) {......}
    }
    //解析完成後進行監聽器啟用處理
    Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
    getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

總結

  • IOC容器初始化入口是在構造方法中呼叫refresh開始的。
  • 通過ResourceLoader來完成資原始檔位置的定位,DefaultResourceLoader是預設的實現,同時上下文字身就給除了ResourceLoader的實現。
  • 建立的IOC容器是DefaultListableBeanFactory。
  • IOC對Bean的管理和依賴注入功能的實現是通過對其持有的BeanDefinition進行相關操作來完成的。
  • 通過BeanDefinitionReader來完成定義資訊的解析和Bean資訊的註冊。
  • XmlBeanDefinitionReader是BeanDefinitionReader的實現了,通過它來解析xml配置中的bean定義。
  • 實際的處理過程是委託給BeanDefinitionParserDelegate來完成的。得到Bean的定義資訊,這些資訊在Spring中使用BeanDefinition物件來表示。
  • BeanDefinition的註冊是由BeanDefinitionRegistry實現的registerBeanDefiition方法進行的。內部使用ConcurrentHashMap來儲存BeanDefiition。