前言
Spring配置檔案讀取流程本來是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,這兩天在看Spring自定義標籤的時候,感覺對Spring配置檔案讀取流程還是研究得不夠,因此將Spring配置檔案讀取流程部分從之前的文章拆出來單獨成為一文。
為了看一下Spring配置檔案載入流程,先定義一個bean.xml:
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是什麼並不重要,有配置檔案就夠了。
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()方法進去:
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中的方法:
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定義的規則:
- 預設的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 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 }
對這個方法逐行總結一下:
- 第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 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 }
簡單說這個方法做了幾件事情:
- beanDefinitionNames新增BeanName
- beanDefinitionMap新增一對對映,Key為BeanName,Value為Bean定義
- 第38行的方法用於重置一下所有本地快取了的Bean定義
<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 }
這段程式碼的邏輯很容易看懂,即:
- 假如是innerBean(比如Spring AOP產生的Bean),使用【類全路徑+#+物件HashCode的16進位制】的格式來命名Bean
- 假如不是innerBean,使用【類全路徑+#+數字】的格式來命名Bean,其中數字指的是,同一個Bean出現1次,只要該Bean沒有id,就從0開始依次向上累加,比如a.b.c#0、a.b.c#1、a.b.c#2
接著看一下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。