Spring IOC(二)容器初始化


本系列目錄:

Spring IOC(一)概覽

Spring IOC(二)容器初始化

Spring IOC(三)依賴注入

Spring IOC(四)總結

 

目錄

一、ApplicationContext接口設計

二、深入源碼,看IOC容器初始化

 

===========正文分割線===========

前面一篇概覽了IOC容器的接口設計。

本文從ApplicationContext接口的一個實現類ClassPathXmlApplicationContext入手,分析容器初始化過程。先看一下ApplicationContext接口設計:

一、ApplicationContext接口設計

ApplicationContext是spring中較高級的容器。和BeanFactory類似,它可以加載配置文件中定義的bean,當有請求的時候分配bean。 另外,它增加了企業所需要的功能,比如,從屬性文件解析文本信息和將事件傳遞給所指定的監聽器。接口設計圖如下:

ApplicationContext繼承5個接口:

1.2個核心接口:

ListableBeanFactory:支持獲取bean 工廠的所有bean實例

HierarchicalBeanFactory:支持繼承關系

2.3個拓展接口:

MessageSource:提供國際化支持

ApplicationEventPublisher:支持事件驅動模型中的事件發布器,這些事件和Bean的生命周期的結合為Bean的管理提供了便利。

ResourcePatternResolver:資源解析器

 

常見實現類:

1.FileSystemXmlApplicationContext:從指定文件地址的加載xml定義的bean

2.ClassPathXmlApplicationContext:從類路徑下載入xml定義的bean

3.XmlWebApplicationContext:web 應用程序的范圍內載入xml定義的bean

二、深入源碼,看IOC容器初始化

為了方便理解和追蹤代碼,使用常用實現類ClassPathXmlApplicationContext寫了一個小例子,步驟如下:

1).在類路徑下新建xml,定義一個bean,其中daoImpl就是bean的名字,spring.aop.xml.dao.impl.DaoImpl對應具體的一個pojo.

 1 <bean id="daoImpl" class="spring.aop.xml.dao.impl.DaoImpl" /> 

2).main方法中直接載入xml,然后獲取bean,最后執行bean實例的方法。

1     public static void main(String[] args) {
2         //源碼入口,從類路徑下讀取xml
3         ApplicationContext ac1 = new ClassPathXmlApplicationContext("aop.xml");
4         Dao dao = (Dao)ac1.getBean("daoImpl");//根據名稱獲取Bean
5         dao.select();//執行Bean實例方法    
}

下面我們就分析ClassPathXmlApplicationContext源碼,來看看都做了什么。

2.1ClassPathXmlApplicationContext類圖

DefaultResourceLoader,該類設置classLoader,並且將配置文件 封裝為Resource文件。

AbstractApplicationContext,該類完成了大部分的IOC容器初始化工作,同時也提供了擴展接口留給子類去重載。該類的refresh()函數是核心初始化操作。

AbstractRefreshableApplicationContext,該類支持刷新BeanFactory。

AbstractRefreshableConfigApplicationContext,該類保存了配置文件路徑

AbstractXmlApplicationContext:該類支持解析bean定義文件

最后ClassPathXmlApplicationContext:只提供了一個簡單的構造函數

Spring 將類職責分開,形成職責鏈,每一層次的擴展 都只是添加了某個功能

然后父類定義大量的模板,讓子類實現,父類層層傳遞到子類 直到某個子類重載了抽象方法。這里應用到了職責鏈設計模式和模板設計模式,IOC是個容器工廠設計模式。

 

2.2 回顧上面的小例子,new ClassPathXmlApplicationContext("aop.xml");這行代碼做了什么?

1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
2         throws BeansException {
3 
4     super(parent);//把ApplicationContext作為父容器,上述測試類中由於直接載入的xml,沒有父容器所以實際傳了null
5 setConfigLocations(configLocations);//替換${}后設置配置路徑 6 if (refresh) { 7 refresh();//核心方法 8 } 9 }

ClassPathXmlApplicationContext的refresh()實際上就是調用了AbstractApplicationContextrefresh()方法。全方法被synchronized同步塊鎖住,源碼如下:

 1 public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             //准備刷新的上下文環境,例如對系統屬性或者環境變量進行准備及驗證。
 4             prepareRefresh();
 5 
 6             //啟動子類的refreshBeanFactory方法.解析xml
 7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 8 
 9             //為BeanFactory配置容器特性,例如類加載器、事件處理器等.
10             prepareBeanFactory(beanFactory);
11 
12             try {
13                 //設置BeanFactory的后置處理. 空方法,留給子類拓展用。 
14                 postProcessBeanFactory(beanFactory);
15 
16                 //調用BeanFactory的后處理器, 這些后處理器是在Bean定義中向容器注冊的.  
17                 invokeBeanFactoryPostProcessors(beanFactory);
18 
19                 //注冊Bean的后處理器, 在Bean創建過程中調用.  
20                 registerBeanPostProcessors(beanFactory);
21 
22                 //初始化上下文中的消息源,即不同語言的消息體進行國際化處理  
23                 initMessageSource();
24 
25                 //初始化ApplicationEventMulticaster bean,應用事件廣播器
26                 initApplicationEventMulticaster();
27 
28                 //初始化其它特殊的Bean, 空方法,留給子類拓展用。 
29                 onRefresh();
30 
31                 //檢查並向容器注冊監聽器Bean
32                 registerListeners();
33 
34                 //實例化所有剩余的(non-lazy-init) 單例Bean.
35                 finishBeanFactoryInitialization(beanFactory);
36 
37                 //發布容器事件, 結束refresh過程. 
38                 finishRefresh();
39             }
40 
41             catch (BeansException ex) {
42                 if (logger.isWarnEnabled()) {
43                     logger.warn("Exception encountered during context initialization - " +
44                             "cancelling refresh attempt: " + ex);
45                 }
46 
47                 //銷毀已經創建的單例Bean, 以避免資源占用.
48                 destroyBeans();
49 
50                 //取消refresh操作, 重置active標志. 
51                 cancelRefresh(ex);
52 
53                 // Propagate exception to caller.
54                 throw ex;
55             }
56 
57             finally {
58                 //重置Spring的核心緩存
60                 resetCommonCaches();
61             }
62         }
63     }

2.3 Resources定位

refresh方法obtainFreshBeanFactory方法調用了refreshBeanFactory,該方法使用DefaultListableBeanFactory去定位resources資源

 1 protected final void refreshBeanFactory() throws BeansException {
 2         if (hasBeanFactory()) {
 3             destroyBeans();
 4             closeBeanFactory();
 5         }
 6         try {//創建並設置DefaultListableBeanFactory同時調用loadBeanDefinitions載入loadBeanDefinition
 7             DefaultListableBeanFactory beanFactory = createBeanFactory();
 8             beanFactory.setSerializationId(getId());
 9             customizeBeanFactory(beanFactory);
10  loadBeanDefinitions(beanFactory);//核心方法
11             synchronized (this.beanFactoryMonitor) {
12                 this.beanFactory = beanFactory;
13             }
14         }
15         catch (IOException ex) {
16             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
17         }
18     }

loadBeanDefinitions其具體實現在AbstractXmlApplicationContext中,定義了一個Reader作為入參執行載入過程:

 1 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 2         // 為給定的bean工廠創建一個reader
 3         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 4 
 5         // Configure the bean definition reader with this context's
 6         // resource loading environment.
 7         beanDefinitionReader.setEnvironment(this.getEnvironment());
 8         beanDefinitionReader.setResourceLoader(this);
 9         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
10 
11         // Allow a subclass to provide custom initialization of the reader,
12         // then proceed with actually loading the bean definitions.
13         initBeanDefinitionReader(beanDefinitionReader);
14  loadBeanDefinitions(beanDefinitionReader);//核心方法
15     }
loadBeanDefinitions方法如下:
 1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 2         Resource[] configResources = getConfigResources();
 3         if (configResources != null) {
 4             reader.loadBeanDefinitions(configResources);
 5         }
 6         String[] configLocations = getConfigLocations();
 7         if (configLocations != null) {
 8             reader.loadBeanDefinitions(configLocations);
 9         }
10     }

getConfigResources采用模板方法設計模式,具體的實現由子類完成,實際上這里getConfigResources調用的就是子類ClassPathXmlApplicationContext的getConfigResources方法。ClassPathXmlApplicationContext繼承了DefaultResourceLoader,具備了Resource加載資源的功能。至此完成了Resource定位!

2.4 BeanDefinition載入

這里支持2種模式:1.模板匹配多資源,生成Resource[]。2.載入單個資源url絕對地址,生成一個Resource

 1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
 2         ResourceLoader resourceLoader = getResourceLoader();//獲取ResourceLoader資源加載器
 3         if (resourceLoader == null) {
 4             throw new BeanDefinitionStoreException(
 5                     "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
 6         }
 7         // 1.匹配模板解析 ClassPathXmlApplicationContext是ResourcePatternResolver接口的實例
 8         if (resourceLoader instanceof ResourcePatternResolver) {
 9            
10             try {//接口ResourcePatternResolver
11                 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
12                 int loadCount = loadBeanDefinitions(resources);
13                 if (actualResources != null) {
14                     for (Resource resource : resources) {
15                         actualResources.add(resource);
16                     }
17                 }
18                 if (logger.isDebugEnabled()) {
19                     logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
20                 }
21                 return loadCount;
22             }
23             catch (IOException ex) {
24                 throw new BeanDefinitionStoreException(
25                         "Could not resolve bean definition resource pattern [" + location + "]", ex);
26             }
27         }
28         else {
29             // 2.載入單個資源url絕對地址
30             Resource resource = resourceLoader.getResource(location);
31             int loadCount = loadBeanDefinitions(resource);
32             if (actualResources != null) {
33                 actualResources.add(resource);
34             }
35             if (logger.isDebugEnabled()) {
36                 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
37             }
38             return loadCount;
39         }
40     }

loadBeanDefinitions最終調用XmlBeanDefinitionReader.doLoadBeanDefinitions(),如下:

 1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
 2             throws BeanDefinitionStoreException {
 3         try {// 取得xml文件的Document,解析過程是由DocumentLoader完成,默認為DefaultDocumentLoader
 4             Document doc = doLoadDocument(inputSource, resource);
 5             return registerBeanDefinitions(doc, resource);// 啟動對BeanDefinition的詳細解析過程,這個解析會使用到spring的BEAN配置規則
 6         }
 7         catch (BeanDefinitionStoreException ex) {
 8             throw ex;
 9         }
10         catch (SAXParseException ex) {
11             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
12                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
13         }
14         catch (SAXException ex) {
15             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
16                     "XML document from " + resource + " is invalid", ex);
17         }
18         catch (ParserConfigurationException ex) {
19             throw new BeanDefinitionStoreException(resource.getDescription(),
20                     "Parser configuration exception parsing XML from " + resource, ex);
21         }
22         catch (IOException ex) {
23             throw new BeanDefinitionStoreException(resource.getDescription(),
24                     "IOException parsing XML document from " + resource, ex);
25         }
26         catch (Throwable ex) {
27             throw new BeanDefinitionStoreException(resource.getDescription(),
28                     "Unexpected exception parsing XML document from " + resource, ex);
29         }
30     }

registerBeanDefinitions是按照spring的bean配置規則解析,源碼如下:

1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
2         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
3         int countBefore = getRegistry().getBeanDefinitionCount();
4         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 核心方法
5         return getRegistry().getBeanDefinitionCount() - countBefore;
6     }

至此就完成了BeanDefinition的載入,BeanDefinition的載入分為兩個部分,

1.調用xml解析器得到的document對象,但是這個對象並沒有按照spring的bean規則進行解析。

2.DefaultBeanDefinitionDocumentReader的registerBeanDefinitions按照Spring的Bean規則進行解析。

2.5 BeanDefinition解析和注冊

registerBeanDefinitions方法調用了doRegisterBeanDefinitions

 1 protected void doRegisterBeanDefinitions(Element root) {
 2         // Any nested <beans> elements will cause recursion in this method. In
 3         // order to propagate and preserve <beans> default-* attributes correctly,
 4         // keep track of the current (parent) delegate, which may be null. Create
 5         // the new (child) delegate with a reference to the parent for fallback purposes,
 6         // then ultimately reset this.delegate back to its original (parent) reference.
 7         // this behavior emulates a stack of delegates without actually necessitating one.
 8         BeanDefinitionParserDelegate parent = this.delegate;
 9         this.delegate = createDelegate(getReaderContext(), root, parent);
10 
11         if (this.delegate.isDefaultNamespace(root)) {
12             String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
13             if (StringUtils.hasText(profileSpec)) {
14                 String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
15                         profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
16                 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
17                     if (logger.isInfoEnabled()) {
18                         logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
19                                 "] not matching: " + getReaderContext().getResource());
20                     }
21                     return;
22                 }
23             }
24         }
25 
26         preProcessXml(root);
27         parseBeanDefinitions(root, this.delegate);// 從Document的根元素開始進行Bean定義的Document對象
28         postProcessXml(root);
29 
30         this.delegate = parent;
31     }
 1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 2         if (delegate.isDefaultNamespace(root)) {
 3             NodeList nl = root.getChildNodes();
 4             for (int i = 0; i < nl.getLength(); i++) {
 5                 Node node = nl.item(i);
 6                 if (node instanceof Element) {
 7                     Element ele = (Element) node;
 8                     if (delegate.isDefaultNamespace(ele)) {
 9  parseDefaultElement(ele, delegate);
10                     }
11                     else {
12                         delegate.parseCustomElement(ele);
13                     }
14                 }
15             }
16         }
17         else {
18             delegate.parseCustomElement(root);
19         }
20     }
21 
22     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
23         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
24             importBeanDefinitionResource(ele);
25         }
26         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
27             processAliasRegistration(ele);
28         }
29         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
30  processBeanDefinition(ele, delegate);
31         }
32         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
33             // recurse
34             doRegisterBeanDefinitions(ele);
35         }
36     }
processBeanDefinition就是對bean標簽的解析和注冊
 1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
 2         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);// 1.解析
 3         if (bdHolder != null) {
 4             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//代理去裝飾:典型的裝飾器模式
 5             try {
 6                 // 2.向IOC容器注冊Bean定義+bean工廠
 7                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
 8             }
 9             catch (BeanDefinitionStoreException ex) {
10                 getReaderContext().error("Failed to register bean definition with name '" +
11                         bdHolder.getBeanName() + "'", ele, ex);
12             }
13             // 3.觸發注冊事件: spring只提供了EmptyReaderEventListener空實現,如果需要你可以自定義
14             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
15         }
16     }

解析parseBeanDefinitionElement方法就是具體的解析入口。解析elemnent->BeanDefinitionHolder,追蹤parseBeanDefinitionElement:

 1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
 2         String id = ele.getAttribute(ID_ATTRIBUTE);// 獲取id
 3         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);// 獲取name
 4 
 5         List<String> aliases = new ArrayList<String>();// 獲取別名
 6         if (StringUtils.hasLength(nameAttr)) {
 7             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
 8             aliases.addAll(Arrays.asList(nameArr));
 9         }
10 
11         String beanName = id;
12         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
13             beanName = aliases.remove(0);
14             if (logger.isDebugEnabled()) {
15                 logger.debug("No XML 'id' specified - using '" + beanName +
16                         "' as bean name and " + aliases + " as aliases");
17             }
18         }
19 
20         if (containingBean == null) {
21             checkNameUniqueness(beanName, aliases, ele);
22         }
23 
24         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
25         if (beanDefinition != null) {
26             if (!StringUtils.hasText(beanName)) {
27                 try {
28                     if (containingBean != null) {
29                         beanName = BeanDefinitionReaderUtils.generateBeanName(
30                                 beanDefinition, this.readerContext.getRegistry(), true);
31                     }
32                     else {
33                         beanName = this.readerContext.generateBeanName(beanDefinition);
34                         // Register an alias for the plain bean class name, if still possible,
35                         // if the generator returned the class name plus a suffix.
36                         // This is expected for Spring 1.2/2.0 backwards compatibility.
37                         String beanClassName = beanDefinition.getBeanClassName();
38                         if (beanClassName != null &&
39                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
40                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
41                             aliases.add(beanClassName);
42                         }
43                     }
44                     if (logger.isDebugEnabled()) {
45                         logger.debug("Neither XML 'id' nor 'name' specified - " +
46                                 "using generated bean name [" + beanName + "]");
47                     }
48                 }
49                 catch (Exception ex) {
50                     error(ex.getMessage(), ele);
51                     return null;
52                 }
53             }
54             String[] aliasesArray = StringUtils.toStringArray(aliases);
55             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
56         }
57 
58         return null;
59     }

好吧,parseBeanDefinitionElement才是核心方法,追蹤:

 1 public AbstractBeanDefinition parseBeanDefinitionElement(
 2             Element ele, String beanName, BeanDefinition containingBean) {
 3 
 4         this.parseState.push(new BeanEntry(beanName));
 5 
 6         String className = null;
 7         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
 8             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
 9         }
10 
11         try {
12             String parent = null;
13             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
14                 parent = ele.getAttribute(PARENT_ATTRIBUTE);
15             }// 這里生成需要的BeanDefinition對象,為Bean定義信息的載入做准備
16             AbstractBeanDefinition bd = createBeanDefinition(className, parent);
17             // 1.解析<bean>元素屬性
18             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
19             bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));//2.解析description
20             //對各種BEAN元素信息進行解析
21             parseMetaElements(ele, bd);// 3.解析<meta>子元素
22             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//4.解析<lookup-method>子元素
23             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//5.解析<replaced-method>子元素
24 
25             parseConstructorArgElements(ele, bd);//6.解析<constructor-arg>
26             parsePropertyElements(ele, bd);//7.解析<property>
27             parseQualifierElements(ele, bd);//8.解析<qualifier>
28 
29             bd.setResource(this.readerContext.getResource());
30             bd.setSource(extractSource(ele));
31 
32             return bd;
33         }
34         catch (ClassNotFoundException ex) {
35             error("Bean class [" + className + "] not found", ele, ex);
36         }
37         catch (NoClassDefFoundError err) {
38             error("Class that bean class [" + className + "] depends on not found", ele, err);
39         }
40         catch (Throwable ex) {
41             error("Unexpected failure during bean definition parsing", ele, ex);
42         }
43         finally {
44             this.parseState.pop();
45         }
46 
47         return null;
48     }

經過這樣逐層的分析,我們在xml文件中定義的BeanDefinition就被整個載入到IOC容器中,並在容器中建立了數據映射。這些數據結構可以以AbstractBeanDefinition為入口讓IOC容器執行索引,查詢和操作。

注冊registerBeanDefinition方法就是具體的注冊入口。追蹤registerBeanDefinition:

 1 public static void registerBeanDefinition(
 2             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
 3             throws BeanDefinitionStoreException {
 4 
 5         // Register bean definition under primary name.
 6         String beanName = definitionHolder.getBeanName();
 7         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//向IoC容器注冊BeanDefinition
 8 
 9         // 如果解析的BeanDefinition有別名, 向容器為其注冊別名. 
10         String[] aliases = definitionHolder.getAliases();
11         if (aliases != null) {
12             for (String alias : aliases) {
13                 registry.registerAlias(beanName, alias);
14             }
15         }
16     }

registerBeanDefinition具體現類:DefaultListableBeanFactory.registerBeanDefinition方法

 1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 2             throws BeanDefinitionStoreException {
 3 
 4         Assert.hasText(beanName, "Bean name must not be empty");
 5         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 6 
 7         if (beanDefinition instanceof AbstractBeanDefinition) {
 8             try {
 9                 ((AbstractBeanDefinition) beanDefinition).validate();
10             }
11             catch (BeanDefinitionValidationException ex) {
12                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
13                         "Validation of bean definition failed", ex);
14             }
15         }
16 
17         BeanDefinition oldBeanDefinition;
18 
19         oldBeanDefinition = this.beanDefinitionMap.get(beanName);
20         if (oldBeanDefinition != null) {
21             if (!isAllowBeanDefinitionOverriding()) {
22                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
23                         "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
24                         "': There is already [" + oldBeanDefinition + "] bound.");
25             }
26             else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
27                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
28                 if (this.logger.isWarnEnabled()) {
29                     this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
30                             "' with a framework-generated bean definition: replacing [" +
31                             oldBeanDefinition + "] with [" + beanDefinition + "]");
32                 }
33             }
34             else if (!beanDefinition.equals(oldBeanDefinition)) {
35                 if (this.logger.isInfoEnabled()) {
36                     this.logger.info("Overriding bean definition for bean '" + beanName +
37                             "' with a different definition: replacing [" + oldBeanDefinition +
38                             "] with [" + beanDefinition + "]");
39                 }
40             }
41             else {
42                 if (this.logger.isDebugEnabled()) {
43                     this.logger.debug("Overriding bean definition for bean '" + beanName +
44                             "' with an equivalent definition: replacing [" + oldBeanDefinition +
45                             "] with [" + beanDefinition + "]");
46                 }
47             }
48             this.beanDefinitionMap.put(beanName, beanDefinition);
49         }
50         else {
51             if (hasBeanCreationStarted()) {
52                 // Cannot modify startup-time collection elements anymore (for stable iteration)
53                 synchronized (this.beanDefinitionMap) {
54                     this.beanDefinitionMap.put(beanName, beanDefinition);
55                     List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
56                     updatedDefinitions.addAll(this.beanDefinitionNames);
57                     updatedDefinitions.add(beanName);
58                     this.beanDefinitionNames = updatedDefinitions;
59                     if (this.manualSingletonNames.contains(beanName)) {
60                         Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
61                         updatedSingletons.remove(beanName);
62                         this.manualSingletonNames = updatedSingletons;
63                     }
64                 }
65             }
66             else {
67                 // Still in startup registration phase
68                 this.beanDefinitionMap.put(beanName, beanDefinition);
69                 this.beanDefinitionNames.add(beanName);
70                 this.manualSingletonNames.remove(beanName);
71             }
72             this.frozenBeanDefinitionNames = null;
73         }
74 
75         if (oldBeanDefinition != null || containsSingleton(beanName)) {
76             resetBeanDefinition(beanName);
77         }
78     }

完成了BeanDefinition的注冊,就完成了IOC容器的初始化過程。容器的作用就是對這些信息進行處理和維護,這些信息就是容器建立依賴反轉的基礎。

三、總結

本文先介紹ApplicationContext接口設計,再從其一個最常見實現類ClassPathXmlApplicationContext寫了一個小例子,作為源碼追蹤的入口。

追蹤了主要包括Resourse定位、BeanDefinition的載入、解析和注冊3個模塊。至此,容器初始化(Bean已生成)已完成,下一章我們看依賴注入的源碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM