1. 程式人生 > >spring原始碼學習筆記之容器的基本實現(一)

spring原始碼學習筆記之容器的基本實現(一)

## 前言 最近學習了<>受益匪淺,本部落格是對學習內容的一個總結、分享,方便日後自己複習或與一同學習的小夥伴一起探討之用. 建議與原始碼配合使用,效果更嘉,使用的spring版本為5.0.x: [官方原始碼下載](https://github.com/spring-projects/spring-framework/tree/5.0.x) [新增中文註解版原始碼](https://github.com/liuboren0617/spring-resource) 下面正文開始. ## 1. 容器的實現 本文要分享的內容就是從讀取配置檔案到註冊BeanDefinition的過程中spring到底做了怎樣的處理. 下面是一個具體的程式碼示例,首先建立一個物件 ``` public class MyTestBean { private String testStr = "testStr"; public String getTestStr() { return testStr; } public void setTestStr(String testStr) { this.testStr = testStr; } } ``` 第二步建立配置檔案 ```
``` 第三步建立測試用例 ``` public class BeanFactoryTest ( @Test public void testSirnpleLoad() { BeanFactory bf = new XmlBeanFactory (new ClassPathResource ("beanFactoryTest.xml")); MyTestBean bean=(MyTestBean) bf.getBean("myTestBean"); System.out.println(bean.getTestStr()); } } ``` 執行後會正確輸出testStr. 本篇部落格的重點就是關注下列程式碼到底做了什麼 ``` BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml")); ``` ## 2. 原始碼分析 ``` BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml")); ``` 第一步通過ClassPathResource將xml檔案轉換為Resource物件. ``` new ClassPathResource("beanFactoryTest.xml") ``` 這裡不再贅述,直接進入XmlBeanFactory這個類. ``` public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * Create a new XmlBeanFactory with the given resource, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } ``` 這個類很簡單繼承了DefaultListableBeanFactory,有一個私有的成員變數XmlBeanDefinitionReader 和兩個構造方法. 可以看出XmlBeanFactory類本身並沒有複寫父類的方法,**XmlBeanFactory與其父類的區別就在於使用私有成員變數XmlBeanDefinitionReader去讀取資原始檔**. 進入XmlBeanFactory的構造方法 ``` /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } ``` 有一點需要關注一下,就是在執行this.reader.loadBeanDefinitions(resource)前先呼叫了super(parentBeanFactory); 追蹤這行程式碼進入了XmlBeanFactory的父類DefaultListableBeanFactory ``` /** * Create a new DefaultListableBeanFactory with the given parent. * @param parentBeanFactory the parent BeanFactory */ public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) { super(parentBeanFactory); } ``` 發現又是一個super(parentBeanFactory),繼續追,又進入了DefaultListableBeanFactory的父類AbstractAutowireCapableBeanFactory ``` /** * Create a new AbstractAutowireCapableBeanFactory with the given parent. * @param parentBeanFactory parent bean factory, or {@code null} if none */ public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) { this(); setParentBeanFactory(parentBeanFactory); } ``` 現在追進去this()方法,我們需要關注的東西就是裡面的ignoreDependencyInterface()方法 ``` /** * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); } ``` 那麼這個方法是做什麼用的呢? ### 2.1 ignoreDependencyInterface() 首先先明確一下這個方法實現的功能是什麼,然後我們再追蹤一下程式碼 #### 2.1.1 ignoreDependencyInterface()實現的功能 **ignoreDependencyInterface的主要功能是忽略給定介面的自動裝配功能**. 舉例來說當A中有屬性B.那麼當Spring在獲取A的Bean的時候如果其屬性B還沒有初始化,那麼Spring 會自動初始化B,這也是Spring中提供的一個重要特性。但是,某些情況 下,B不會被初始化,其中的一種情況就是B實現了BeanNameAware介面 #### 2.1.2 原始碼部分 接下來看看原始碼部分是如何實現上述功能的 實現分為三個步驟: 1. 存入(將需要過濾的介面存入集合) 2. 過濾Bean的所有屬性(只要Bean的屬性實現了第一步存入的介面進行剔除,返回一個過濾後的屬性的集合) 3. 對過濾後的屬性進行初始化 第一步存入原始碼,直接追蹤ignoreDependencyInterface() ``` /** * Dependency interfaces to ignore on dependency check and autowire, as Set of * Class objects. By default, only the BeanFactory interface is ignored. */ // set 專門存入需要需要過濾的類 private final Set> ignoredDependencyInterfaces = new HashSet<>(); /** * Ignore the given dependency interface for autowiring. *

This will typically be used by application contexts to register * dependencies that are resolved in other ways, like BeanFactory through * BeanFactoryAware or ApplicationContext through ApplicationContextAware. *

By default, only the BeanFactoryAware interface is ignored. * For further types to ignore, invoke this method for each type. * @see org.springframework.beans.factory.BeanFactoryAware * @see org.springframework.context.ApplicationContextAware */ public void ignoreDependencyInterface(Class ifc) { //放入集合 this.ignoredDependencyInterfaces.add(ifc); } ``` 將傳入的值方法一個set集合(對就是ignoredDependencyInterfaces這個集合). 第二步過濾部分的原始碼 ``` /** * Return an array of non-simple bean properties that are unsatisfied. * These are probably unsatisfied references to other beans in the * factory. Does not include simple properties like primitives or Strings. * @param mbd the merged bean definition the bean was created with * @param bw the BeanWrapper the bean was created with * @return an array of bean property names * @see org.springframework.beans.BeanUtils#isSimpleProperty */ protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) { // 方法的第一個引數AbstractBeanDefinition mbd 可以理解為2.1.1舉的例子類A // 聲明瞭一個集合 Set result = new TreeSet<>(); // 拿到beanDefinition的所有屬性 PropertyValues pvs = mbd.getPropertyValues(); // 得到屬性的所有描述 PropertyDescriptor[] pds = bw.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { // 這裡遍歷所有屬性,可以理解為2.1.1舉的例子屬性B // 過濾屬性的方法在!isExcludedFromDependencyCheck(pd) if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) && !BeanUtils.isSimpleProperty(pd.getPropertyType())) { result.add(pd.getName()); } } // 最後返回過濾後的屬性 return StringUtils.toStringArray(result); } ``` 通過原始碼我們知道了具體過濾的邏輯 !isExcludedFromDependencyCheck(pd) 在這個方法中,繼續追蹤 ``` /** * Determine whether the given bean property is excluded from dependency checks. *

This implementation excludes properties defined by CGLIB and * properties whose type matches an ignored dependency type or which * are defined by an ignored dependency interface. * @param pd the PropertyDescriptor of the bean property * @return whether the bean property is excluded * @see #ignoreDependencyType(Class) * @see #ignoreDependencyInterface(Class) */ protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) { return (AutowireUtils.isExcludedFromDependencyCheck(pd) || this.ignoredDependencyTypes.contains(pd.getPropertyType()) || // 這裡使用了第一步存入的集合 AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces)); } ``` 看條件 AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces),這裡使用了我們第一步存入的集合,接近真相了,繼續追蹤 ``` /** * Return whether the setter method of the given bean property is defined * in any of the given interfaces. * @param pd the PropertyDescriptor of the bean property pd就是某個物件的一個屬性 * @param interfaces the Set of interfaces (Class objects) * @return whether the setter method is defined by an interface */ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set> interfaces) { // 這裡得到B的set方法 Method setter = pd.getWriteMethod(); // 如果set方法不為空 if (setter != null) { /* * getDeclaringClass * 該方法返回一個Class物件,返回當前class物件的宣告物件class,一般針對內部類的情況,比如A類有內部類B,那麼通過B.class.getDeclaringClass()方法將獲取到A的Class物件. * 在使用反射物件時比如Method和Field的getDeclaringClass方法將獲取到所屬類物件 * */ // 所以這個targetClass 為所屬類物件 // 這裡得到了屬性B的class Class targetClass = setter.getDeclaringClass(); for (Class ifc : interfaces) { // 判斷 ifc 是否是屬性B的父類(這裡的ifc 則是第一步存入的介面) // 如果 屬性B繼承了第一步存入的介面 並且 存入的介面也有相同的set方法,就會被過濾 if (ifc.isAssignableFrom(targetClass) && // 判斷類是否有指定的public方法; ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) { return true; } } } return false; } ``` 看到上面的程式碼我們明白了過濾屬性的步驟: 1. 得到屬性B的class 2. 遍歷第一步存入的介面的集合 3. 如果屬性B繼承了第一步存入的介面,並且存入的介面也有相同的set方法,就會被過濾 第二部分忽略的邏輯就完了 最後是第三部分使用,使用過濾後的屬性進行初始化 ``` protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } Set autowiredBeanNames = new LinkedHashSet<>(4); // 過濾後的屬性是在這裡使用的 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don't try autowiring by type for type Object: never makes sense, // even if it technically is a unsatisfied, non-simple property. if (Object.class != pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } } protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { // 過濾後的屬性是在這裡使用的 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { if (containsBean(propertyName)) { Object bean = getBean(propertyName); pvs.add(propertyName, bean); registerDependentBean(propertyName, beanName); if (logger.isDebugEnabled()) { logger.debug("Added autowiring by name from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + propertyName + "'"); } } else { if (logger.isTraceEnabled()) { logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + "' by name: no matching bean found"); } } } } ``` 第二步過濾後的屬性是用來注入屬性的無論是通過name還是通過type注入. 經過一系列的追蹤我們證實了2.1.1節的功能實現. ### 2.2 XmlBeanDefinitionReader 構造方法解釋完畢了,進入正題,xmlBeanFactory使用私有成員變數XmlBeanDefinitionReader去載入資源 ``` /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } ``` 進入this.reader.loadBeanDefinitions(),發現建立了一個EncodedResource物件,傳入loadBeanDefinitions()方法 ``` /** * Load bean definitions from the specified XML file. * @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 */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } ``` 進入loadBeanDefinitions(new EncodedResource(resource))方法 ``` /** * 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); } // 獲得當前正在載入的資源 Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(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(); } } } ``` 這個方法主要做了兩個操作: 1. 檢測資源是否迴圈載入,如果是則丟擲異常 2. 對inputSource設定編碼. 繼續往下追蹤,進入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法. ``` /** * 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 { // 通過inputSource, resource獲得文件物件 Document doc = doLoadDocument(inputSource, resource); // 獲取到document後註冊BeanDefinition 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); } } ``` doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法通過inputSource、 resource獲得了Document這個方法就不展開解釋了,繼續往下追蹤進入了registerBeanDefinitions(doc, resource) ``` /** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. *

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 { //1. 生成BeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 返回註冊列表中註冊的bean的數量 int countBefore = getRegistry().getBeanDefinitionCount(); //2. 通過生成的BeanDefinitionDocumentReader註冊beanDefinition documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 這裡返回的是新註冊的bean的數量 //3. 返回最新的註冊的bean的數量 return getRegistry().getBeanDefinitionCount() - countBefore; } ``` registerBeanDefinitions(Document doc, Resource resource)主要做了三件事: 1. 生成BeanDefinitionDocumentReader ``` private Class documentReaderClass = DefaultBeanDefinitionDocumentReader.class; /** * Create the {@link BeanDefinitionDocumentReader} to use for actually * reading bean definitions from an XML document. *

The default implementation instantiates the specified "documentReaderClass". * @see #setDocumentReaderClass */ protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanUtils.instantiateClass(this.documentReaderClass); } ``` 可以看出這裡生成的BeanDefinitionDocumentReader實際上是DefaultBeanDefinitionDocumentReader型別的 2. 通過生成的BeanDefinitionDocumentReader註冊beanDefinition(也就是我們下一節要分析的主角) ``` documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); ``` 這裡要留意一下createReaderContext(resource)方法 ``` /** * Create the {@link XmlReaderContext} to pass over to the document reader. */ public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); } ``` 注意第五個引數this,相當於把自己(XmlBeanDefinitionReader)也傳遞給了BeanDefinitionDocumentReader,方便後續呼叫 3. 返回最新的註冊的bean的數量 ``` return getRegistry().getBeanDefinitionCount() - countBefore; ``` 這裡的getRegistry()是哪裡來的,當時有點懵,找了一會才明白... 是這麼來的 ``` public class XmlBeanFactory extends DefaultListableBeanFactory { // 看XmlBeanDefinitionReader構造方法的引數this private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * Create a new XmlBeanFactory with the given resource, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } } ``` 嗯... 答案就在XmlBeanDefinitionReader的構造方法中 ``` /** * Create new XmlBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */ public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } ``` 這裡劃重點,因為後續得到的BeanDefinition還需要使用registry(實際上是XmlBeanFactory)去註冊BeanDefinition XmlBeanDefinitionReader實現的功能到這裡告一段落了, 進入下一個類DefaultBeanDefinitionDocumentReader ### 2.3 DefaultBeanDefinitionDocumentReader ``` /** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). *

Opens a DOM Document; then initializes the default settings * specified at the {@code

} level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } ``` 這個很簡單沒什麼說的,通過document得到了Element,然後呼叫doRegisterBeanDefinitions(root) ``` /** * Register each bean definition within the given root {@code
} element. */ protected void doRegisterBeanDefinitions(Element root) { // Any nested elements will cause recursion in this method. In // order to propagate and preserve 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); // 主要邏輯就是判斷這個配置檔案是否是允許存在,不存在則 return,不再浪費時間解析了 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); /*解析bean的定義*/ parseBeanDefinitions(root, this.delegate); /*解析後處理,該方法為空實現*/ postProcessXml(root); this.delegate = parent; } ``` 上述程式碼做了三件事: 1. 方法中生成了一個BeanDefinitionParserDelegate,這個delegate有大用處,主要用來將Element解析成BeanDefinition(什麼是BeanDefinition? 可以理解為將xml中的 bean標籤 解析成一個物件,bean標籤的屬性與BeanDefinition的屬性一一對應). 2. 對配置檔案的profile屬性的校驗 舉個例子,有多個profile(多個環境)的情況,如果 param-value標籤和profile的值能對應的情況才往下解析,否則就return了. ``` spring.profiles.default development ``` 3. 有三個方法解析前處理、解析bean的定義、解析後處理,**其中解析前處理和解析後處理兩個方法為空實現,如果需要做一些特殊的操作可以自行實現**. 重點關注解析bean的定義parseBeanDefinitions(root, this.delegate)方法 ``` /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * * @param root the DOM root element of the document */ 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); } } ``` 進入解析bean的定義parseBeanDefinitions(root, this.delegate)方法,可以看到這裡進行了一個判斷,判斷這個element究竟是自定義標籤還是預設標籤,並且如果是預設標籤的情況還會進行遍歷,再次判斷是自定義標籤還是預設標籤,**說明了預設標籤中有可能包含自定義標籤.** 那什麼是預設標籤? 什麼是自定義標籤? 預設標籤是spring自己定義的,比如: ``` ``` 自定義標籤: ``` ``` **這兩種標籤的解析方式有很大的不同**. 我們先追蹤解析預設標籤的方法 ``` private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // import 標籤 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // alias標籤 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //bean標籤 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //beans標籤 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } ``` 可以看到有四種預設標籤,分別是: import標籤(匯入其他配置檔案)、alias標籤(設定別名)、bean標籤、beans標籤(巢狀的bean). 限於篇幅及重要程度,我們重點關注processBeanDefinition(ele, delegate)方法,理解了bean標籤的解析相信其他三類標籤也可以觸類旁通. 進入processBeanDefinition(ele, delegate)方法 ``` /** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { /* * 首先委託BeanDefinitionDelegate 類的parseBeanDefinitionElement 方法進行元素解析, 返回BeanDefinitionHolder 型別的例項bdHolder ,經過這個方法後,bdHolder 例項已經包含我 們配置檔案中配置的各種屬性了,例如class 、name、id 、alias 之類的屬性 * */ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { /* * 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要 再次對自定義標籤進行解析 * */ bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. /* * 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了 * BeanDefinitionReaderUtils的registerBeanDefinition方法 * */ 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)); } } ``` 這個方法主要做了四件事: 1. 使用delegate解析Element生成BeanDefinitionHolder物件 2. 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析 3. 解析完成後,需要對解析後的bdHolder 進行註冊 4. 傳送註冊事件 接下來詳細分析一下這四個步驟 #### 2.3.1 生成BeanDefinitionHolder物件 我們來看看BeanDefinitionHolder物件的生成過程 ``` BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); ``` 追蹤進去: ``` /** * Parses the supplied {@code } element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } ``` 繼續: ``` /** * Parses the supplied {@code } element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 1. 提取元素中的id 以及name 屬性。 String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_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); } // 2. 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { //3 . 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName 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); // 4 . 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } ``` 這裡又做了四件事,具體執行的地方已經新增到上面原始碼的註釋中了: 1. 提取元素中的id 以及name 屬性。 2. 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。 3. 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName 。 4. 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中 這裡我們重點關注第二步的程式碼 ``` AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); ``` **這一步實現的功能就是將xml配置檔案中bean標籤的屬性都放到GenericBeanDefinition 型別的例項中。** 進入 parseBeanDefinitionElement(ele, beanName, containingBean)方法: ``` /** * Parse the bean definition itself, without regard to name or aliases. May return * {@code null} if problems occurred during the parsing of the bean definition. */ @Nullable public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; // 解析class 屬性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; //解析parent 屬性 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { // 創始用於承載屬性的AbstractBeanDefinition 型別的GenericBeanDefintiion AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 硬編碼解析預設bean的各種屬性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 解析元資料 parseMetaElements(ele, bd); // 解析lookup-method屬性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 解析replace-method 屬性 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析建構函式引數 parseConstructorArgElements(ele, bd); // 解析property子元素 parsePropertyElements(ele, bd); //解析Qualifier子元素 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; } ``` 可以看到先生成了AbstractBeanDefinition物件(實際上是GenericBeanDefinition) ``` // 1. 生成AbstractBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 2. 追蹤createBeanDefinition(className, parent); /** * Create a bean definition for the given class name and parent name. * @param className the name of the bean class * @param parentName the name of the bean's parent bean * @return the newly created bean definition * @throws ClassNotFoundException if bean class resolution was attempted but failed */ protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); } // 3. 繼續追蹤BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); /** * Create a new GenericBeanDefinition for the given parent name and class name, * eagerly loading the bean class if a ClassLoader has been specified. * @param parentName the name of the parent bean, if any * @param className the name of the bean class, if any * @param classLoader the ClassLoader to use for loading bean classes * (can be {@code null} to just register bean classes by name) * @return the bean definition * @throws ClassNotFoundException if the bean class could not be loaded */ public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { // 真相了,實際上是GenericBeanDefinition GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setParentName(parentName); if (className != null) { if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; } ``` 生成了AbstractBeanDefinition物件之後,就該解析bean標籤的各種屬性到AbstractBeanDefinition,例如: meta、replace-method、lookup-method、constructor-arg等等,這裡我們就以解析建構函式引數(constructor-arg)方法為例,看看spring到底是如何解析bean的屬性的 ``` //1.分析的示例, 解析建構函式 parseConstructorArgElements(ele, bd); // 2.追蹤進去 /** * Parse constructor-arg sub-elements of the given bean element. */ public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { // 得到標籤的所有節點 NodeList nl = beanEle.getChildNodes(); // 遍歷所有節點 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // 第一個條件 node是Element型別並且(node是預設標籤或node的父結點不是預設標籤) // 第二個條件這個節點是constructor-arg if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { // 進入預設標籤解析 parseConstructorArgElement((Element) node, bd); } } } //3. isCandidateElement(node) 條件追蹤 private boolean isCandidateElement(Node node) { return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode()))); } ``` 進一步閱讀原始碼前,我們先簡單瞭解一下constructor-arg的使用,方便我們更好的理解原始碼,示例: ``` first parameter second parameter ``` 當判斷Element是constructor-arg正式進入parseConstructorArgElement((Element) node, bd)構造方法解析: ``` /** * Parse a constructor-arg element. */ public void parseConstructorArgElement(Element ele, BeanDefinition bd) { // 得到index屬性 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); // 得到type屬性 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); // 得到name屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 當index屬性存在 if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(index)); //解析ele 對應的屬性元素 Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); ///不允許重複指定相同引數 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } else { // index屬性不存在,自動尋找 try { this.parseState.push(new ConstructorArgumentEntry()); Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } } ``` 上述程式碼會使用ConstructorArgumentValues.ValueHolder 型別來封裝解析出來的元素,將type 、name 和index(**上面if/esle的判斷區別在這,如果有index屬性會一起封裝進去,沒有會忽略**) 屬性一併封裝在ConstructorArgumentValues.ValueHolder 型別中並新增至當前BeanDefinition 的constructorArgumentValues的indexedArgumentValues屬性中 再關注一下上述程式碼中解析constructor-arg的屬性值的方法 ``` /** * Get the value of a property element. May be a list etc. * Also used for constructor arguments, "propertyName" being null in this case. */ @Nullable public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null ? " element for property '" + propertyName + "'" : " element"); // Should only have one child element: ref, value, list, etc. // 一個屬性只能對應一種型別:ref 、value 、list 等 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { // 這個node 是constructor-arg標籤的子標籤如等 Node node = nl.item(i); // /對應description 或者meta 不處理 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element) node; } } } // 解析constructor-arg上的ref屬性 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { /* * 1. 同時既有ref屬性又有value屬性 2 . 存在ref 屬性或者value 屬性且又有子元素 * 報錯 * */ error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } if (hasRefAttribute) { // ref 屬性的處理,使用RuntimeBeanReference 封裝對應的ref 名稱 String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { // value 屬性的封裝,使用TypedStringValue 封裝 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null) { // 解析子元素 return parsePropertySubElement(subElement, bd); } else { // Neither child element nor "ref" or "value" attribute found. // 既沒有ref 也沒有value 也沒有子元素,Spring 蒙圈了 error(elementName + " must specify a ref or value", ele); return null; } } ``` 從程式碼上來看,對建構函式中屬性元素的解析,經歷了以下幾個過程。 1. 略過description 或者meta 。 2. 提取constructor-arg 上的ref和 value 屬性,以便於根據規則驗證正確性,其規則為在 constructor-arg 上**不存在以下情況**。 a. 同時既有ref 屬性又有value 屬性。 b. 存在ref 屬性或者value 屬性且又有子元素。 3. ref 屬性的處理。使用RuntimeBeanReference 封裝對應的ref名稱,如: ``` ``` 4. value 屬性的處理。使用TypedStringValue 封裝,如: ``` ``` 5. 子元素的處理 ``` ``` 解析建構函式引數的方法到這裡就結束了.bean的其他屬性的解析也是大同小異的. 這裡再提一下replace-method和lookup-method屬性的解析(不明白這兩個屬性幹什麼用的,請自行百度一下) 以lookup-method屬性為例 ``` // 解析lookup-method屬性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); /** * Parse lookup-override sub-elements of the given bean element. */ public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { Element ele = (Element) node; // 獲取要要修飾的方法 String methodName = ele.getAttribute(NAME_ATTRIBUTE); // 獲取配置返回的bean String beanRef = ele.getAttribute(BEAN_ELEMENT); LookupOverride override = new LookupOverride(methodName, beanRef); override.setSource(extractSource(ele)); // 注意這行程式碼,將override新增到了AbstractBeanDefinition 中的methodOverrides 屬性中 // 後續需要對這個override進行校驗 overrides.addOverride(override); } } } ``` replace-method和lookup-method屬性解析後獲得LookupOverride,**然後將override新增到了AbstractBeanDefinition 中的methodOverrides屬性中,後續需要對這個override進行校驗**. ``` BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); ``` 上述程式碼中的重要邏輯,已經分析完了,再回憶一下delegate.parseBeanDefinitionElement(ele)都做了那四件事: 1. 提取元素中的id 以及name 屬性。 2. 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。 3. 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName 。 4. 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中 現在已經生成並返回了BeanDefinitionHolder, 接下來回到processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法的第二步: **當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析** #### 2.3.2 BeanDefinitionHolder的自定義標籤解析 預設標籤中含有自定義標籤的示例: ``` ``` 重新看看processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法 ``` /** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { /* * 首先委託BeanDefinitionDelegate 類的parseBeanDefinitionElement 方法進行元素解析, 返回BeanDefinitionHolder 型別的例項bdHolder ,經過這個方法後,bdHolder 例項已經包含我 們配置檔案中配置的各種屬性了,例如class 、name、id 、alias 之類的屬性 * */ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { /* * 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要 再次對自定義標籤進行解析 * */ bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. /* * 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了 * BeanDefinitionReaderUtils的registerBeanDefinition方法 * */ 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)); } } ``` 我們接下來要分析的方法就是 ``` /* * 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要 再次對自定義標籤進行解析 * */ bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); ``` 追蹤: ``` /** * Decorate the given bean definition through a namespace handler, if applicable. * @param ele the current element * @param originalDef the current bean definition * @return the decorated bean definition */ public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) { /* * 這裡將函式中第三個引數設定為空,那麼第三個引數是做什麼用的呢?什麼情況下不為空 呢?其實這第三個引數是父類bean ,當對某個巢狀配置進行分析時,這裡需要傳遞父類 beanDefinition 。分析原始碼得知這裡傳遞的引數其實是為了使用父類的scope 屬性,以備子類若 沒有設定scope 時預設使用父類的屬性,這裡分析的是頂層配置,所以傳遞null * */ return decorateBeanDefinitionIfRequired(ele, originalDef, null); } ``` 繼續: ``` /** * Decorate the given bean definition through a namespace handler, if applicable. * @param ele the current element * @param originalDef the current bean definition * @param containingBd the containing bean definition (if any) * @return the decorated bean definition */ public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = originalDef; // Decorate based on custom attributes first. //遍歷所有的屬性,看看是否有適用於修飾的屬性 NamedNodeMap attributes = ele.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node node = attributes.item(i); finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } // Decorate based on custom nested elements. // 邊歷所有的子節點,看看是否有適用於修飾的子元素 NodeList children = ele.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } } return finalDefinition; } ``` 看原始碼,這裡進行了兩次遍歷裝飾,第一次是對所有屬性進行遍歷,第二次是對所有子節點進行裝飾. 進入裝飾的方法decorateIfRequired(node, finalDefinition, containingBd): ``` /** * Decorate the given bean definition through a namespace handler, * if applicable. * @param node the current child node * @param originalDef the current bean definition * @param containingBd the containing bean definition (if any) * @return the decorated bean definition */ public BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { // 根據節點獲取到namespaceUri String namespaceUri = getNamespaceURI(node); // 如果namespaceUri不為空 同時不是預設的名稱空間 if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { // 獲得名稱空間解析器解析namespaceUri 得到NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); // 如果處理器不為空 if (handler != null) { // 進行裝飾 BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); if (decorated != null) { return decorated; } } else if (namespaceUri.startsWith("http://www.springframework.org/")) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); } else { // A custom namespace, not to be handled by Spring - maybe "xml:...". if (logger.isDebugEnabled()) { logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); } } } // 最後返回(裝飾好的)BeanDefinitionHolder return originalDef; } ``` 該方法的大致流程就是: 1. 先獲取到namespaceUri 2. 根據名稱空間解析器解析namespaceUri獲取到NamespaceHandler 3. 使用NamespaceHandler對BeanDefinitionHolder進行裝飾 4. 返回裝飾後的BeanDefinitionHolder 現在我們也明白了自定義標籤的解析流程了 #### 2.3.3 註冊BeanDefinition 到了到了,我們已經獲取到了BeanDefinitionHolder,並且如果BeanDefinitionHolder中含有自定義標籤的話又進行了一次解析.現在已經得到了最終的BeanDefinitionHolder,接下來就是要將BeanDefinitionHolder註冊了(快要結束了). ``` // Register the final decorated instance. /* * 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了 * BeanDefinitionReaderUtils的registerBeanDefinition方法 * */ BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); /** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. // beanName註冊 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 alias : aliases) { registry.registerAlias(beanName, alias); } } } ``` registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法有兩個引數,第一個引數是我們剛剛獲得到BeanDefinitionHolder,而第二個引數這個 getReaderContext().getRegistry() 實際上就是XMLBeanFactory本身. registerBeanDefinition方法本身有兩種註冊方式,第一種是通過beanName註冊,第二種是通過別名註冊 通過BenName註冊 ``` // 實現方法是DefaultListableBeanFactory類的registerBeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); @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 { /* * 1. 校驗: 註冊前的最後一次校驗,這裡的校驗不同於之前的XML檔案校驗, * 主要是對於AbstractBeanDefinition屬性中的methodOverrides校驗, * 校驗methodsOverrides是否與工廠方法並存或者methodsOverrides對應的方法根本不存在 * */ ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } // 2. 嘗試獲取BeanDefinition BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { // 3. 對beanName 已經註冊的情況的處理。如果設定了不允許bean 的覆蓋,則需要丟擲 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + existingDefinition + "] bound."); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isWarnEnabled()) { logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isInfoEnabled()) { logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } // 註冊bean就是將它放到下面的map裡 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 4. 如果從快取中得到的BeanDefinition為null,判斷是否已經開始建立,如果是則給快取加上鎖新增到快取,否則直接新增到快取 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) // 因為beanDefinitionMap 是全域性變數,這裡會存在併發訪問的情況 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // this.manualSingletonNames 手工註冊的單例物件的名稱列表,按註冊順序排列 // 從this.manualSingletonNames中移除BeanName if (this.manualSingletonNames.contains(beanName)) { Set updatedSingletons = new LinkedHashSet<>(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; } // 5. 清除解析之前留下的對應beanName的快取 if (existingDefinition != null || containsSingleton(beanName)) { // 重置beanName的快取 resetBeanDefinition(beanName); } else if (isConfigurationFrozen()) { clearByTypeCache(); } } ``` 上面的程式碼中我們看到,在對於bean 的註冊處理方式上,主要進行了幾個步驟。 1. 對AbstractBeanDefinition的屬性methodOverrides的校驗. 2. 通過beanName從快取中獲取BeanDefinition 3. 如果從快取中得到的BeanDefinition不為null, 對beanName 已經註冊的情況的處理。如果設定了不允許bean 的覆蓋,則需要丟擲異常 4. 如果從快取中得到的BeanDefinition為null,判斷是否已經開始建立,如果是則給快取加上鎖新增到快取,否則直接新增到快取 5. 清除解析之前留下的對應beanName的快取 接下來是通過別名註冊 ``` registry.registerAlias(beanName, alias); @Override public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { ///如果beanName 與alias 相同的話不記錄alias ,並刪除對應的alias if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { String registeredName = this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } // 如果alias 不允許被被蓋則丟擲異常 if (!allowAliasOverriding()) { throw new IllegalStateExc