為了弄清楚Bean是怎么來的,花費了大把功夫,現在要把Bean Definition的加載、解析、處理、注冊到bean工廠的過程記下來。這只是bean definition 的加載、解析、處理、注冊過程中的一種。
好記性不如爛筆頭。
首先我已經知道bean definition 存在了哪里:它就存在一個Map對象中,如果使用的是DefaultListableBeanFactory的話,它就存在一個ConcurrentHashMap對象中。
/** bean 的名字為鍵,BeanDefinition為值,初始容量為256 */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
現在將BeanDefinition是如何一步步走到這個Map對象中的。
一、BeanDefinition的加載
過程比較長,得對着源碼慢慢看。
這里講的是使用xml配置文件的方式來配置bean.
既然是使用的xml配置文件的方式來配置bean,那么首先要讀取xml文件。
一)、AbstractBeanDefinitionReader類中
這一步只是說加載bean definition,但是這里方法參數resources 所指的資源並不確定是什么樣的,有可能是xml文件,也有可能是屬性文件,或者是腳本。
@Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { counter += loadBeanDefinitions(resource);//這個方法有幾個實現,如下 } return counter; }
上面的代碼中的一行 counter += loadBeanDefinitions(resource); 這行代碼有好幾個實現類都實現了它。如下圖:
我這里使用實現類XmlBeanDefinitionReader中的實現,所以這里的資源是xml文件。
二)、XmlBeanDefinitionReader類中
從指定的xml文件中加載bean definition,但是他這里只是將Resource轉化為EncodedResource對象。然后把這個任務遞交給它的重載方法來做。
三)、XmlBeanDefinitionReader類中
從指定的XML文件中加載bean definition。但還不是真正的加載bean定義,只是做准備。
/** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ 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(); } } }
四、XmlBeanDefinitionReader類中
這里是真正的從xml 文件中加載的bean definition 。主要有兩個步驟:①、加載指定的文檔,得到一個Document對象。②、將Document對象和Resource交給registerBeanDefinitions(...)方法來完成注冊
/** * Actually load bean definitions from the specified XML file. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument * @see #registerBeanDefinitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { //省略捕獲異常的代碼 } }
五)、XmlBeanDefinitionReader類中
注冊指定的Document對象中的bean definition。這里實際上是創建了一個BeanDefinitionDocumentReader對象然后讓它來完成。
/** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
二、BeanDefinition的解析
六)、DefaultBeanDefinitionDocumentReader類中
DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的一個實現類。這一步主要是解析文檔解析的是<beans/>。比如xml文件是XSD的還是DTD的。
這里主要是獲得一個Element對象。這個對象就代表xml文件中的<beans/>節點。在這個節點下包含着文件中的所有<bean/>節點。然后將這個Element對像交給的doRegistrerBeanDefinitions(Element)方法來處理。
七)、DefaultBeanDefinitionDocumentReader類中
這個方法將</beans>節點下的每一個<bean/>相對應的bean definition注冊。但是真正做這件事的是另一個方法 parseBeanDefinitions(root, this.delegate);
不過在調用parseBeanDefinitions(root,this.delegate)方法之前和之后都可以對這個這個方法參數中的Element對象進行處理。
/** * Register each bean definition within the given root {@code <beans/>} element. */ protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
八、DefaultBeanDefinitionDocumentReader類中
用一個for循環遍歷<beans/>節點下的所有子節點,也就是所有的<bean/>,然后對<bean/>節點進行解析。注意,剛才是對<beans/>進行解析。不過這個解析的任務交給parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate)方法來完成。
九)、DefaultBeanDefinitionDocumentReader類中
這個方法用到了遞歸,因為bean是可以嵌套的,所以<beans/>節點下的每一個<bean/>節點都可能在嵌套有很多bean。所以它會判斷這個bean是不是嵌套bean,如果不是就進行處理,如果是就進行遞歸。
處理bean元素的任務交給了三個方法,如圖。我這里用processBeanDefinition(ele,delegate)方法舉例。
三、BeanDefinition的處理
十)、DefaultBeanDefinitionDocumentReader類中
在這個方法中它將的Element對象轉化成了BeanDefinitionHolder對象。這個BeanDefinitionHolder對象中持有的BeanDefinition實例的引用,還有beanName,還有bean的別名。
然后將BeanDefinitionHolder對象和特定的bean工廠作為參數交個BeanDefinitionReaderUtils類來處理來進行注冊。
第301行代碼中如何創建BeanDefinitionHolder的??BeanDefinition的創建
四、BeanDefinition的注冊
十一)、BeanDefinitionReaderUtils類中
這個方法先根據BeanDefinitionHolder獲取beanName和BeanDefinition,然后將其注冊到Bean工廠中Map對象中。比如bean工廠是DefaultListableBeanFactory。
上面紅色方框中的代碼 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());這個方法有好幾個實現類實現了它,如下圖:
我選擇了DefaultListableBeanFactory
十二)、DefaultListableBeanFactory類中,在這個方法中,將BeanDefinition 注冊到了開頭所說的ConcurrentHashMap對象中了。
//--------------------------------------------------------------------- // Implementation of BeanDefinitionRegistry interface //--------------------------------------------------------------------- @Override 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); } } BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
五、總結
BeanDefinition的加載、解析、處理、注冊主要涉及到了四個類。
①、XMLBeanDefinitionReader:主要是的任務是把XML文件加載到內存中以Document對象的形式存在。
②、DefaultBeanDefinitionDocumentReader:完成解析和處理的任務。最后將處理得到的BeanDefinitionHolder交給了BeanDefinitionReaderUtils進行注冊。
③、BeanDefinitionReaderUtils:BeanDefinitionHolder有了,Bean工廠也有了,它就負責把BeanDefinitionHolder中的BeanDefinition和BeanName等取出來,然后注冊到Bean工廠中。
④、DefaultListableBeanFactory(bean工廠):它有一個ConcurrentHashMap成員變量,以beanName為鍵,BeanDefinition為值保存注冊的bean。