前言
Spring配置文件讀取流程原本是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一塊兒的,這兩天在看Spring自定義標籤的時候,感受對Spring配置文件讀取流程仍是研究得不夠,所以將Spring配置文件讀取流程部分從以前的文章拆出來單獨成爲一文。html
爲了看一下Spring配置文件加載流程,先定義一個bean.xml:node
1spring 2c# 3緩存 4數據結構 5dom 6ide 7函數 8源碼分析 9 10 11 12 13 14 15 |
<? xml version = "1.0" encoding = "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> < bean id = "beanPostProcessorBean" class = "org.xrq.action.BeanPostProcessorBean" /> < bean id = "beanFactoryPostProcessorBean" class = "org.xrq.action.BeanFactoryPostProcessorBean" /> < bean id = "multiFunctionBean" class = "org.xrq.action.MultiFunctionBean" init-method = "initMethod" > < property name = "propertyA" value = "abc" /> </ bean > </ beans > |
至於Bean是什麼並不重要,有配置文件就夠了。
Bean定義加載流程—-從Refresh到Bean定義加載前
首先看一下Bean加載前整個代碼流程走向。Spring上下文刷新始於AbstractApplicationContext的refresh()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); ... } |
代碼不全帖了,第7行的obtainFreshBeanFactory()方法進去:
1 2 3 4 5 6 7 8 |
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug( "Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } |
第2行的refreshBeanFactory()方法進去,它是AbstractApplicationContext的子類AbstractRefreshableApplicationContext中的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized ( this .beanFactoryMonitor) { this .beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException( "I/O error parsing bean definition source for " + getDisplayName(), ex); } } |
首先第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 2 3 4 5 6 7 8 9 10 11 12 13 14 |
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setResourceLoader( this ); beanDefinitionReader.setEntityResolver( new ResourceEntityResolver( this )); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } |
第3行的XmlBeanDefinitionReader是Bean加載的核心類,先構建出來,後面代碼沒什麼值得看的,直接看第13行代碼,傳入XmlBeanDefinitionReader:
1 2 3 4 5 6 7 8 9 10 |
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); } } |
由第8行的代碼進去,這個就不跟了,直接走到XmlBeanDefinitionReader的父類AbstractBeanDefinitionReader的loadBeanDefinitions方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null ) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available" ); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null ) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug( "Loaded " + loadCount + " bean definitions from location pattern [" + location + "]" ); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]" , ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null ) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]" ); } return loadCount; } } |
咱們研究Spring加載流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的實現類,進入第8行的判斷,走第12行的方法,這裏也不跟了,很簡單,最終代碼走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定義加載的開始。
Bean定義加載流程—-Bena定義的存儲
上面說到了Bean定義是存儲在DefaultListableBeanFactory中的,咱們來看一下具體代碼:
1 2 3 4 5 |
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); /** List of bean definition names, in registration order */ 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null" ); if (logger.isInfoEnabled()) { logger.info( "Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this .resourcesCurrentlyBeingLoaded.get(); if (currentResources == null ) { currentResources = new HashSet<EncodedResource>( 4 ); this .resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!" ); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null ) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this .resourcesCurrentlyBeingLoaded.remove(); } } } |
第17行根據XML文件獲取輸入字節流,接着流程走到23行doLoadBeanDefinitions方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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; } 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); } } |
首先是第4行,獲取驗證模式,代碼不跟了,最終出來的是DetectMode,DetectMode的意思是XML文件的驗證模式由XML文件自己決定,若是是DTD那就使用DTD驗證,若是是XSD就使用XSD驗證。
接着是第5行~第6行,這兩行的做用是經過DOM獲得org.w3c.dom.Document對象,Document將XML文件當作一棵樹,Dociument即對這顆樹數據結構的一個描述。
最近進入第7行,繼續加載Bean定義的流程,跟一下registerBeanDefinitions方法:
1 2 3 4 5 6 7 |
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // Read document based on new BeanDefinitionDocumentReader SPI. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } |
由於是每一個XML文件執行一次registerBeanDefinitions方法註冊Bean定義,所以這整個方法的返回值表示的是當前XML裏面一共註冊了多少個Bean。直接進入第5行的代碼,使用BeanDefintionDocumentReader的registerBeanDefinitions方法來註冊Bean定義:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this .readerContext = readerContext; logger.debug( "Loading bean definitions" ); Element root = doc.getDocumentElement(); BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); preProcessXml(root); parseBeanDefinitions(root, delegate); postProcessXml(root); } |
這裏面的方法,第9行的方法preProcessXml是個空方法,留給子類擴展用;第11行的方法postProcessXml是個空方法,留給子類擴展用。
第5行的方法獲得根節點,也就是<beans …></beans>。
剩下的就是第7行的createHelper方法與第10行的parseBeanDefintions方法了,前者構造出一個Bean定義解析器的委託類,後者使用委託類解析Bean定義,下面分兩部分分別來看。
Bean定義加載流程—-createHelper
先看createHelper,即根據根節點建立一個Bean定義解析器的委託類,看一下代碼實現:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this .readerContext = readerContext; logger.debug( "Loading bean definitions" ); Element root = doc.getDocumentElement(); BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); preProcessXml(root); parseBeanDefinitions(root, delegate); postProcessXml(root); } |
第2行沒有什麼特別的,new一個BeanDefinitionParserDelegate出來,第3行的代碼跟一下,用於設置默認屬性:
1 2 3 4 |
public void initDefaults(Element root) { populateDefaults( this .defaults, root); this .readerContext.fireDefaultsRegistered( this .defaults); } |
跟一下第2行的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) { defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE)); defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE)); defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE)); defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); } if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); } if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); } defaults.setSource( this .readerContext.extractSource(root)); } |
看到就是這個地方將<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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } |
首先說一下,這裏的Namespace都是默認的Namespace,至於Namespace的問題,和自定義Spring標籤相關,我想放到自定義Spring標籤部分說,這裏只要知道代碼會進入第2行與第9行的判斷便可。
第2行的判斷進去,都在遍歷Element下的節點不看了,直接跟第9行的代碼parseDefaultElement:
1 2 3 4 5 6 7 8 9 10 11 |
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); } } |
這邊就是判斷節點名稱是import仍是alias仍是bean,是其中任意一個就進入相應的執行邏輯,import和alias不看了,這裏就看Bean加載流程部分,也就是第9行的processBeanDefinition方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null ) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to register bean definition with name '" + bdHolder.getBeanName() + "'" , ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder)); } } |
先看第4行,第4行的意思是在須要的時候裝飾Bean定義,好比AOP的場景會使用到,這個留在AOP的時候看這段代碼。
再看第7行,第7行的意思是註冊Bean定義,這在下一部分說,屬於Bean定義加載流程的最後一步。
如今看來第2行的代碼,顧名思義即解析Bean定義元素,跟一下代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove( 0 ); if (logger.isDebugEnabled()) { logger.debug( "No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases" ); } } if (containingBean == null ) { checkNameUniqueness(beanName, aliases, ele); } AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null ) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null ) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this .readerContext.getRegistry(), true ); } else { beanName = this .readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && ! this .readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug( "Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]" ); } } catch (Exception ex) { error(ex.getMessage(), ele); return null ; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null ; } |
第2行~第18行的代碼都是用於獲取BeanName,從這段邏輯中咱們能夠總結出BeanName定義的規則:
- 默認的BeanName就是<bean>標籤重定義的id
- <bean>標籤中能夠定義name屬性,一個bean能夠有多個別名(alias),都定義在name屬性中,不一樣的別名以」,;」分割,假如beanId未定義,那麼就以name屬性中的第一個別名做爲beanName
第20行~第22行的代碼主要用於確保BeanName的惟一性,跟一下第21行的方法就知道,BeanName與Bean別名都會放在Set<String>中,而後每次加載Bean定義的時候都會去這個Set<String>中檢查當前BeanName和Bean別名是否存在,若是存在就報錯。
接着進入第24行的代碼,開始解析Bean定義元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this .parseState.push( new BeanEntry(beanName)); String className = null ; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null ; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource( this .readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error( "Bean class [" + className + "] not found" , ele, ex); } catch (NoClassDefFoundError err) { error( "Class that bean class [" + className + "] depends on not found" , ele, err); } catch (Throwable ex) { error( "Unexpected failure during bean definition parsing" , ele, ex); } finally { this .parseState.pop(); } return null ; } |
對這個方法逐行總結一下:
- 第4行,ParseState是一個基於棧的簡單結構,在解析的流程中用於追蹤邏輯上的位置,裏面存放的是Entry接口。Entry接口有各類實現類好比AdviceEntry、AdvisorEntry、BeanEntry、PropertyEntry等,每一步操做開始將一個Entry推送至棧頂,每一步操做結束將Entry從棧頂彈出,這裏將一個BeanEntry推送至棧頂,標識解析Bean定義開始
- 第6行~第9行的代碼,用於獲取Bean對應的類路徑
- 第12行~第15行的代碼,用於解析<bean>標籤中的parent屬性
- 第16行的代碼,根據Bean對應的類路徑以及parent,生成一個AbstractBeanDefinition,AbstractBeanDefinition是一個抽象類,生成出來的具體實例爲GenericBeanDefinition
- 第18行的代碼,就不跟了進去一看就很好理解,是用於解析Bean定義的屬性的,包括scope、sington、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destory-method、factory-method、factory-bean,createHelper部分說了這個方法會建立一個名爲defaults的DocumentDefaultsDefinition,像lazy-init、autowire-candidate、init-method、destory-method未定義時都會嘗試從DocumentDefaultsDefinition中獲取
- 第19行用於設置Bean描述
- 第21行的方法parseMetaElements用於解析META元素
- 第22行的方法parseLookupOverrideSubElements用於解析<bean>標籤下的<lookup-method>標籤
- 第23行的方法parseReplacedMethodSubElements用於解析<bean>標籤下的<replaced-method>標籤,不過這個和<lookup-method>標籤好像不太經常使用
- 第25行的方法parseConstructorArgElements用於解析<bean>標籤下的<constructor-arg>標籤,<constructor-arg>標籤用於實現構造函數注入Bean屬性
- 第26行的方法parsePropertyElements用於解析<bean>標籤下的<property>標籤,<property>標籤是最多見的Bean屬性注入的方式
- 第27行的方法parseQualifierElements用於解析<bean>標籤下的<qualifier>標籤,使用<qualifier>標籤也是Spring屬性注入的一種方式,不過不太經常使用
這樣,就把整個Bean定義加載的流程跟完了,最後一步,就是將AbstractBeanDefinition寫回到DefaultListableBeanFactory中了。
Bean定義加載流程—-Bean定義寫回DefaultListableBeanFactory
最後一步,將Bean定義寫回DefaultListableBeanFactory中。代碼要追溯回DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null ) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to register bean definition with name '" + bdHolder.getBeanName() + "'" , ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder)); } } |
Bean定義加載完畢後構造爲一個BeanDefinitionHolder,第4行的代碼以前說過的,用於在必要的狀況下裝飾Bean定義先無論。
第7行的代碼用於註冊Bean定義,跟一下代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null ) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } } |
跟一下第7行的方法,調用DefaultListableBeanFactory的registerBeanDefinition方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty" ); Assert.notNull(beanDefinition, "BeanDefinition must not be null" ); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed" , ex); } } synchronized ( this .beanDefinitionMap) { Object oldBeanDefinition = this .beanDefinitionMap.get(beanName); if (oldBeanDefinition != null ) { if (! this .allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound." ); } else { if ( this .logger.isInfoEnabled()) { this .logger.info( "Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]" ); } } } else { this .beanDefinitionNames.add(beanName); this .frozenBeanDefinitionNames = null ; } this .beanDefinitionMap.put(beanName, beanDefinition); resetBeanDefinition(beanName); } } |
簡單說這個方法作了幾件事情:
- beanDefinitionNames添加BeanName
- beanDefinitionMap添加一對映射,Key爲BeanName,Value爲Bean定義
- 第38行的方法用於重置一下全部本地緩存了的Bean定義
<bean>中不定義id及id重複場景Spring的處理方式
這兩天又想到了兩個細節問題,<bean>中不定義id或者id重複,這兩種場景Spring是如何處理的。首先看一下不定義id的場景,代碼在BeanDefinitionParserDelegate類第398行的這個判斷這裏:
1 2 3 4 5 6 7 8 9 10 11 |
if (beanDefinition != null ) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null ) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this .readerContext.getRegistry(), true ); } else { beanName = this .readerContext.generateBeanName(beanDefinition); ... } |
當bean的id未定義時,即beanName爲空,進入第2行的if判斷。containingBean能夠看一下,這裏是由方法傳入的,是一個null值,所以進入第9行的判斷,即beanName由第9行的方法生成,看一下生成方式,代碼最終要追蹤到BeanDefinitionReaderUtils的generateBeanName方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public static String generateBeanName( BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { String generatedBeanName = definition.getBeanClassName(); if (generatedBeanName == null ) { if (definition.getParentName() != null ) { generatedBeanName = definition.getParentName() + "$child" ; } else if (definition.getFactoryBeanName() != null ) { generatedBeanName = definition.getFactoryBeanName() + "$created" ; } } if (!StringUtils.hasText(generatedBeanName)) { throw new BeanDefinitionStoreException( "Unnamed bean definition specifies neither " + "'class' nor 'parent' nor 'factory-bean' - can't generate bean name" ); } String id = generatedBeanName; if (isInnerBean) { // Inner bean: generate identity hashcode suffix. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); } else { // Top-level bean: use plain class name. // Increase counter until the id is unique. int counter = - 1 ; while (counter == - 1 || registry.containsBeanDefinition(id)) { counter++; id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; } } return id; } |
這段代碼的邏輯很容易看懂,即:
- 假如是innerBean(好比Spring AOP產生的Bean),使用【類全路徑+#+對象HashCode的16進制】的格式來命名Bean
- 假如不是innerBean,使用【類全路徑+#+數字】的格式來命名Bean,其中數字指的是,同一個Bean出現1次,只要該Bean沒有id,就從0開始依次向上累加,好比a.b.c#0、a.b.c#一、a.b.c#2
接着看一下id重複的場景Spring的處理方式,重複id是這樣的,Spring使用XmlBeanDefinitionReader讀取xml文件,在這個類的doLoadBeanDefinitions的方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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。