前言html
Spring配置文件讀取流程原本是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一塊兒的,這兩天在看Spring自定義標籤的時候,感受對Spring配置文件讀取流程仍是研究得不夠,所以將Spring配置文件讀取流程部分從以前的文章拆出來單獨成爲一文。node
爲了看一下Spring配置文件加載流程,先定義一個bean.xml:spring
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <bean id="beanPostProcessorBean" class="org.xrq.action.BeanPostProcessorBean" /> 8 9 <bean id="beanFactoryPostProcessorBean" class="org.xrq.action.BeanFactoryPostProcessorBean" /> 10 11 <bean id="multiFunctionBean" class="org.xrq.action.MultiFunctionBean" init-method="initMethod"> 12 <property name="propertyA" value="abc" /> 13 </bean> 14 15 </beans>
至於Bean是什麼並不重要,有配置文件就夠了。c#
Bean定義加載流程----從Refresh到Bean定義加載前緩存
首先看一下Bean加載前整個代碼流程走向。Spring上下文刷新始於AbstractApplicationContext的refresh()方法:數據結構
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 prepareRefresh(); 5 6 // Tell the subclass to refresh the internal bean factory. 7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 ... 13 }
代碼不全帖了,第7行的obtainFreshBeanFactory()方法進去:dom
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 refreshBeanFactory(); 3 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 4 if (logger.isDebugEnabled()) { 5 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 6 } 7 return beanFactory; 8 }
第2行的refreshBeanFactory()方法進去,它是AbstractApplicationContext的子類AbstractRefreshableApplicationContext中的方法:ide
1 @Override 2 protected final void refreshBeanFactory() throws BeansException { 3 if (hasBeanFactory()) { 4 destroyBeans(); 5 closeBeanFactory(); 6 } 7 try { 8 DefaultListableBeanFactory beanFactory = createBeanFactory(); 9 beanFactory.setSerializationId(getId()); 10 customizeBeanFactory(beanFactory); 11 loadBeanDefinitions(beanFactory); 12 synchronized (this.beanFactoryMonitor) { 13 this.beanFactory = beanFactory; 14 } 15 } 16 catch (IOException ex) { 17 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 18 } 19 }
首先第8行獲取DefaultListableBeanFactory,而後執行第11行的方法,傳入當前獲取的BeanFactory,準備加載Bean定義。BeanFactory中有存儲了些什麼數據在【Spring源碼分析】Bean加載流程概覽一文中有畫表格詳細說明,看過表格的朋友應該知道爲何第8行要獲取的是DefaultListableBeanFactory而不是它的接口BeanFactory,由於Bean定義存儲在Map<String, BeanDefinition>中,這個Map的位置就是在DefaultListableBeanFactory裏,所以這裏直接獲取DefaultListableBeanFactory並做爲參數層層向後傳,加載完Bean定義後直接向Map<String, BeanDefinition>裏put鍵值對。函數
看下loadBeanDefinitions方法,它是AbstractRefreshableApplicationContext子類AbstractXmlApplicationContext中的一個方法:源碼分析
1 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 2 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 3 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 4 5 // Configure the bean definition reader with this context's 6 // resource loading environment. 7 beanDefinitionReader.setResourceLoader(this); 8 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 9 10 // Allow a subclass to provide custom initialization of the reader, 11 // then proceed with actually loading the bean definitions. 12 initBeanDefinitionReader(beanDefinitionReader); 13 loadBeanDefinitions(beanDefinitionReader); 14 }
第3行的XmlBeanDefinitionReader是Bean加載的核心類,先構建出來,後面代碼沒什麼值得看的,直接看第13行代碼,傳入XmlBeanDefinitionReader:
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 }
由第8行的代碼進去,這個就不跟了,直接走到XmlBeanDefinitionReader的父類AbstractBeanDefinitionReader的loadBeanDefinitions方法中:
1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { 2 ResourceLoader resourceLoader = getResourceLoader(); 3 if (resourceLoader == null) { 4 throw new BeanDefinitionStoreException( 5 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); 6 } 7 8 if (resourceLoader instanceof ResourcePatternResolver) { 9 // Resource pattern matching available. 10 try { 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 // Can only load single resources by absolute 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 }
咱們研究Spring加載流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的實現類,進入第8行的判斷,走第12行的方法,這裏也不跟了,很簡單,最終代碼走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定義加載的開始。
Bean定義加載流程----Bena定義的存儲
上面說到了Bean定義是存儲在DefaultListableBeanFactory中的,咱們來看一下具體代碼:
1 /** Map of bean definition objects, keyed by bean name */ 2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); 3 4 /** List of bean definition names, in registration order */ 5 private final List<String> beanDefinitionNames = new ArrayList<String>();
最終DefaultListableBeanFactory會先遍歷beanDefinitionNames,從beanDefinitionMap中拿到對應的BeanDefinition,最終轉爲具體的Bean對象。BeanDefinition自己是一個接口,AbstractBeanDefinition這個抽象類存儲了Bean的屬性,看一下AbstractBeanDefinition這個抽象類的定義:
這個類的屬性與方法不少,這裏就列舉了一些最主要的方法和屬性,能夠看到包含了bean標籤中的全部屬性,以後就是根據AbstractBeanDefinition中的屬性值構造出對應的Bean對象。
Bean定義加載流程----開始加載Bean定義
上面一部分的結尾說道,Bean定義加載的開始始於XmlBeanDefinitionReader的loadBeanDefinitions方法,看下loadBeanDefinitions方法定義:
1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 2 Assert.notNull(encodedResource, "EncodedResource must not be null"); 3 if (logger.isInfoEnabled()) { 4 logger.info("Loading XML bean definitions from " + encodedResource.getResource()); 5 } 6 7 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 8 if (currentResources == null) { 9 currentResources = new HashSet<EncodedResource>(4); 10 this.resourcesCurrentlyBeingLoaded.set(currentResources); 11 } 12 if (!currentResources.add(encodedResource)) { 13 throw new BeanDefinitionStoreException( 14 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 15 } 16 try { 17 InputStream inputStream = encodedResource.getResource().getInputStream(); 18 try { 19 InputSource inputSource = new InputSource(inputStream); 20 if (encodedResource.getEncoding() != null) { 21 inputSource.setEncoding(encodedResource.getEncoding()); 22 } 23 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 24 } 25 finally { 26 inputStream.close(); 27 } 28 } 29 catch (IOException ex) { 30 throw new BeanDefinitionStoreException( 31 "IOException parsing XML document from " + encodedResource.getResource(), ex); 32 } 33 finally { 34 currentResources.remove(encodedResource); 35 if (currentResources.isEmpty()) { 36 this.resourcesCurrentlyBeingLoaded.remove(); 37 } 38 } 39 }
第17行根據XML文件獲取輸入字節流,接着流程走到23行doLoadBeanDefinitions方法:
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 try { 4 int validationMode = getValidationModeForResource(resource); 5 Document doc = this.documentLoader.loadDocument( 6 inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 7 return registerBeanDefinitions(doc, resource); 8 } 9 catch (BeanDefinitionStoreException ex) { 10 throw ex; 11 } 12 catch (SAXParseException ex) { 13 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 14 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 15 } 16 catch (SAXException ex) { 17 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 18 "XML document from " + resource + " is invalid", ex); 19 } 20 catch (ParserConfigurationException ex) { 21 throw new BeanDefinitionStoreException(resource.getDescription(), 22 "Parser configuration exception parsing XML from " + resource, ex); 23 } 24 catch (IOException ex) { 25 throw new BeanDefinitionStoreException(resource.getDescription(), 26 "IOException parsing XML document from " + resource, ex); 27 } 28 catch (Throwable ex) { 29 throw new BeanDefinitionStoreException(resource.getDescription(), 30 "Unexpected exception parsing XML document from " + resource, ex); 31 } 32 }
首先是第4行,獲取驗證模式,代碼不跟了,最終出來的是DetectMode,DetectMode的意思是XML文件的驗證模式由XML文件自己決定,若是是DTD那就使用DTD驗證,若是是XSD就使用XSD驗證。
接着是第5行~第6行,這兩行的做用是經過DOM獲得org.w3c.dom.Document對象,Document將XML文件當作一棵樹,Dociument即對這顆樹數據結構的一個描述。
最近進入第7行,繼續加載Bean定義的流程,跟一下registerBeanDefinitions方法:
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 2 // Read document based on new BeanDefinitionDocumentReader SPI. 3 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 4 int countBefore = getRegistry().getBeanDefinitionCount(); 5 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 6 return getRegistry().getBeanDefinitionCount() - countBefore; 7 }
由於是每一個XML文件執行一次registerBeanDefinitions方法註冊Bean定義,所以這整個方法的返回值表示的是當前XML裏面一共註冊了多少個Bean。直接進入第5行的代碼,使用BeanDefintionDocumentReader的registerBeanDefinitions方法來註冊Bean定義:
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 2 this.readerContext = readerContext; 3 4 logger.debug("Loading bean definitions"); 5 Element root = doc.getDocumentElement(); 6 7 BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); 8 9 preProcessXml(root); 10 parseBeanDefinitions(root, delegate); 11 postProcessXml(root); 12 }
這裏面的方法,第9行的方法preProcessXml是個空方法,留給子類擴展用;第11行的方法postProcessXml是個空方法,留給子類擴展用。
第5行的方法獲得根節點,也就是<beans ...></beans>。
剩下的就是第7行的createHelper方法與第10行的parseBeanDefintions方法了,前者構造出一個Bean定義解析器的委託類,後者使用委託類解析Bean定義,下面分兩部分分別來看。
Bean定義加載流程----createHelper
先看createHelper,即根據根節點建立一個Bean定義解析器的委託類,看一下代碼實現:
1 protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) { 2 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); 3 delegate.initDefaults(root); 4 return delegate; 5 }
第2行沒有什麼特別的,new一個BeanDefinitionParserDelegate出來,第3行的代碼跟一下,用於設置默認屬性:
1 public void initDefaults(Element root) { 2 populateDefaults(this.defaults, root); 3 this.readerContext.fireDefaultsRegistered(this.defaults); 4 }
跟一下第2行的代碼:
1 protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) { 2 defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE)); 3 defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE)); 4 defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE)); 5 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); 6 if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { 7 defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); 8 } 9 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { 10 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); 11 } 12 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { 13 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); 14 } 15 defaults.setSource(this.readerContext.extractSource(root)); 16 }
看到就是這個地方將<beans>標籤下的default-lazy-init、default_merge、default_autowire、default-dependency-check、default-autowire-candidates、default-init-method、default-destroy-method這幾個屬性取出來,設置到DocumentDefaultsDefinition即defaults中。
Bean定義加載流程----parseBeanDefintions
到了parseBeanDefintions方法了,這個方法開始真正遍歷XML文件中的各個標籤並轉換爲對應的Bean定義,看一下方法定義:
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 }
首先說一下,這裏的Namespace都是默認的Namespace,至於Namespace的問題,和自定義Spring標籤相關,我想放到自定義Spring標籤部分說,這裏只要知道代碼會進入第2行與第9行的判斷便可。
第2行的判斷進去,都在遍歷Element下的節點不看了,直接跟第9行的代碼parseDefaultElement:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 3 importBeanDefinitionResource(ele); 4 } 5 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 6 processAliasRegistration(ele); 7 } 8 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 9 processBeanDefinition(ele, delegate); 10 } 11 }
這邊就是判斷節點名稱是import仍是alias仍是bean,是其中任意一個就進入相應的執行邏輯,import和alias不看了,這裏就看Bean加載流程部分,也就是第9行的processBeanDefinition方法:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 3 if (bdHolder != null) { 4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 5 try { 6 // Register the final decorated instance. 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 // Send registration event. 14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 15 } 16 }
先看第4行,第4行的意思是在須要的時候裝飾Bean定義,好比AOP的場景會使用到,這個留在AOP的時候看這段代碼。
再看第7行,第7行的意思是註冊Bean定義,這在下一部分說,屬於Bean定義加載流程的最後一步。
如今看來第2行的代碼,顧名思義即解析Bean定義元素,跟一下代碼:
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { 2 String id = ele.getAttribute(ID_ATTRIBUTE); 3 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 4 5 List<String> aliases = new ArrayList<String>(); 6 if (StringUtils.hasLength(nameAttr)) { 7 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_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 }
第2行~第18行的代碼都是用於獲取BeanName,從這段邏輯中咱們能夠總結出BeanName定義的規則:
第20行~第22行的代碼主要用於確保BeanName的惟一性,跟一下第21行的方法就知道,BeanName與Bean別名都會放在Set<String>中,而後每次加載Bean定義的時候都會去這個Set<String>中檢查當前BeanName和Bean別名是否存在,若是存在就報錯。
接着進入第24行的代碼,開始解析Bean定義元素:
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 } 16 AbstractBeanDefinition bd = createBeanDefinition(className, parent); 17 18 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 19 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); 20 21 parseMetaElements(ele, bd); 22 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); 23 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); 24 25 parseConstructorArgElements(ele, bd); 26 parsePropertyElements(ele, bd); 27 parseQualifierElements(ele, bd); 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 }
對這個方法逐行總結一下:
這樣,就把整個Bean定義加載的流程跟完了,最後一步,就是將AbstractBeanDefinition寫回到DefaultListableBeanFactory中了。
Bean定義加載流程----Bean定義寫回DefaultListableBeanFactory
最後一步,將Bean定義寫回DefaultListableBeanFactory中。代碼要追溯回DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 3 if (bdHolder != null) { 4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 5 try { 6 // Register the final decorated instance. 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 // Send registration event. 14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 15 } 16 }
Bean定義加載完畢後構造爲一個BeanDefinitionHolder,第4行的代碼以前說過的,用於在必要的狀況下裝飾Bean定義先無論。
第7行的代碼用於註冊Bean定義,跟一下代碼:
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()); 8 9 // Register aliases for bean name, if any. 10 String[] aliases = definitionHolder.getAliases(); 11 if (aliases != null) { 12 for (String aliase : aliases) { 13 registry.registerAlias(beanName, aliase); 14 } 15 } 16 }
跟一下第7行的方法,調用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 synchronized (this.beanDefinitionMap) { 18 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); 19 if (oldBeanDefinition != null) { 20 if (!this.allowBeanDefinitionOverriding) { 21 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 22 "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + 23 "': There is already [" + oldBeanDefinition + "] bound."); 24 } 25 else { 26 if (this.logger.isInfoEnabled()) { 27 this.logger.info("Overriding bean definition for bean '" + beanName + 28 "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); 29 } 30 } 31 } 32 else { 33 this.beanDefinitionNames.add(beanName); 34 this.frozenBeanDefinitionNames = null; 35 } 36 this.beanDefinitionMap.put(beanName, beanDefinition); 37 38 resetBeanDefinition(beanName); 39 } 40 }
簡單說這個方法作了幾件事情:
<bean>中不定義id及id重複場景Spring的處理方式
這兩天又想到了兩個細節問題,<bean>中不定義id或者id重複,這兩種場景Spring是如何處理的。首先看一下不定義id的場景,代碼在BeanDefinitionParserDelegate類第398行的這個判斷這裏:
1 if (beanDefinition != null) { 2 if (!StringUtils.hasText(beanName)) { 3 try { 4 if (containingBean != null) { 5 beanName = BeanDefinitionReaderUtils.generateBeanName( 6 beanDefinition, this.readerContext.getRegistry(), true); 7 } 8 else { 9 beanName = this.readerContext.generateBeanName(beanDefinition); 10 ... 11 }
當bean的id未定義時,即beanName爲空,進入第2行的if判斷。containingBean能夠看一下,這裏是由方法傳入的,是一個null值,所以進入第9行的判斷,即beanName由第9行的方法生成,看一下生成方式,代碼最終要追蹤到BeanDefinitionReaderUtils的generateBeanName方法:
1 public static String generateBeanName( 2 BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) 3 throws BeanDefinitionStoreException { 4 5 String generatedBeanName = definition.getBeanClassName(); 6 if (generatedBeanName == null) { 7 if (definition.getParentName() != null) { 8 generatedBeanName = definition.getParentName() + "$child"; 9 } 10 else if (definition.getFactoryBeanName() != null) { 11 generatedBeanName = definition.getFactoryBeanName() + "$created"; 12 } 13 } 14 if (!StringUtils.hasText(generatedBeanName)) { 15 throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " + 16 "'class' nor 'parent' nor 'factory-bean' - can't generate bean name"); 17 } 18 19 String id = generatedBeanName; 20 if (isInnerBean) { 21 // Inner bean: generate identity hashcode suffix. 22 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); 23 } 24 else { 25 // Top-level bean: use plain class name. 26 // Increase counter until the id is unique. 27 int counter = -1; 28 while (counter == -1 || registry.containsBeanDefinition(id)) { 29 counter++; 30 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; 31 } 32 } 33 return id; 34 }
這段代碼的邏輯很容易看懂,即:
接着看一下id重複的場景Spring的處理方式,重複id是這樣的,Spring使用XmlBeanDefinitionReader讀取xml文件,在這個類的doLoadBeanDefinitions的方法中:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } ... }
第5行的代碼將xml解析成Document,這裏的解析使用的是JDK自帶的DocumentBuilder,DocumentBuilder處理xml文件輸入流,發現兩個<bean>中定義的id重複即會拋出XNIException異常,最終將致使Spring容器啓動失敗。
所以,結論就是:Spring不容許兩個<bean>定義相同的id。