本篇文章主要介紹 Spring IoC 容器怎麼加載 bean
的定義元信息。java
下圖是一個大體的流程圖:node
第一次畫圖,畫的有點爛。😂git
首先定義一個簡單的 POJO,以下:github
public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
再編寫一個 XML 文件。web
<?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.xsd"> <bean id="user" class="com.leisurexi.ioc.domain.User"> <property name="id" value="1"/> <property name="name" value="leisurexi"/> </bean> </beans>
最後再來一個測試類。spring
@Test public void test(){ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("META-INF/spring-bean.xml"); User user = beanFactory.getBean("user", User.class); System.out.println(user); }
上面這段代碼比較簡單,無非就是聲明 bean
工廠,而後經過指定的 XML 文件加載 bean
的定義元信息,最後經過 bean
工廠獲取 bean
。express
首先咱們來了解一下 DefaultListableBeanFactory
,下面是該類的類圖及層次結構。數組
alias
的簡單增刪改等操做。map
做爲 alias
的緩存,並對接口 AliasRegistry
進行實現。bean
及 bean
的各類屬性。SingletonBeanRegistry
各函數的實現。BeanFactory
,也就是在 BeanFactory
定義的功能的基礎上增長了對 parentBeanFactory
的支持。BeanDefinition
的各類增刪改操做。DefaultSingletonBeanRegistry
基礎上增長了對 FactoryBean
的特殊處理功能。BeanFactory
的各類方法。BeanFactory
提供了獲取多個 bean
的各類方法。FactoryBeanRegistrySupport
和 ConfigurableBeanFactory
的功能。bean
、自動注入、初始化以及應用 bean
的後處理器。AbstractBeanFactory
並對接口 AutowireCapableBeanFactory
進行實現。BeanFactory
配置清單,指定忽略類型及接口等。bean
註冊後的處理。能夠看到上面的接口大多數是定義了一些功能或在父接口上擴展了一些功能,DefaultListableBeanFactory
實現了全部接口,大多數默認狀況下咱們所使用的 beanFactory
就是 DefaultListableBeanFactory
。緩存
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { // 獲取 resourceLoader,這邊是 PathMatchingResourcePatternResolver ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } // 判斷 resourceLoader 是不是 ResourcePatternResolver,咱們這邊是符合的 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 根據路徑獲取所欲符合的配置文件並封裝成 Resource 對象 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 根據 Resource 加載 bean definition,並返回數量,見下面詳解 int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } 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); // 根據 Resource 加載 bean definition,並返回數量,見下面詳解 int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
上面方法主要是將資源文件轉換爲 Resource
對象,而後調用 loadBeanDefinitions(Resource...)
加載 BeanDefinition
。安全
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int count = 0; for (Resource resource : resources) { // 加載 BeanDefinition,見下文詳解 count += loadBeanDefinitions(resource); } return count; }
該方法主要就是遍歷 resources
而後調用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource)
。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // 將 Resource 封裝成 EncodedResource,也就是對資源指定編碼和字符集 return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } // 當前正在加載的 EncodedResource Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 若是當前 EncodedResource 以及存在,表明出現了循環加載,拋出異常 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 獲取 Resource 的輸入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 將 inputStream 封裝成 org.xml.sax.InputSource InputSource inputSource = new InputSource(inputStream); // 若是 encodedResource 的編碼不爲空,設置 inputSource 的編碼 if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 加載 BeanDefinition (方法以 do 開頭,真正處理的方法) return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 關閉流 inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { // 當前資源以及加載完畢,從 currentResources 中移除 currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
上面主要將 Resource
封裝成 EncodedResource
,也就是制定資源的編碼和字符集。而後獲取 Resource
的輸入流 InputStream
,並封裝成 InputSource
設置其編碼,最終調用 doLoadBeanDefinitions
開始真正的加載流程。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 根據 inputSource 和 resource 加載 XML 文件,並封裝成 Document,見下文詳解 Document doc = doLoadDocument(inputSource, resource); // 用 doc 去解析和註冊 bean definition,見下文詳解 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } 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); } }
上面代碼拋開異常處理,邏輯很是簡單,就是用 inputSource
和 resource
加載 XML 文件,並封裝成 Document
對象,而後去註冊 BeanDefinition
。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); } // 獲取 XML 文件的驗證模式 protected int getValidationModeForResource(Resource resource) { // 若是手動指定了驗證模式則使用指定的驗證模式 int validationModeToUse = getValidationMode(); if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } // 若是未指定則使用自動檢測,其實就是判斷文件是否包含 DOCTYPE,若是 int detectedMode = detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). // 若是沒有找到驗證,默認使用 XSD 模式,由於 DTD 已經不維護了 return VALIDATION_XSD; } // DefaultDocumentLoader.java // 這裏就是使用 DocumentLoader 去加載 XML 文件。首先建立 DocumentBuilderFactory,再經過 DocumentBuilderFactory 建立 DocumentBuilder,進而解析 inputSource 來返回 Document 對象 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) { logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
detectValidationMode()
方法其實就是讀取文件內容,判斷是否包含 DOCTYPE
,若是包含就是 DTD 不然就是 XSD。
獲取 XML 配置文件的驗證模式。XML 文件的驗證模式是用來保證 XML 文件的正確性,常見的驗證模式有 DTD 和 XSD。
DTD XML 格式示例:
STD XML 格式示例:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 獲取 DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 獲取註冊中心,再靠註冊中心獲取註冊以前以及註冊過的 BeanDefinition 數量 int countBefore = getRegistry().getBeanDefinitionCount(); // 解析並註冊 BeanDefinition,見下文詳情 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 獲取註冊事後 BeanDefinition 數量減去註冊以前的數量,獲得的就是本次註冊的數量 return getRegistry().getBeanDefinitionCount() - countBefore; }
這裏的 getRegistry()
方法返回的就是 DefaultListableBeanFactory
,由於就只有它實現了 BeanDefinitionRegistry
接口。
DefaultListableBeanFactory
中定義了存放 BeanDefinition
的緩存,因此 getBeanDefinitionCount()
方法返回的就是 beanDefinitionMap
的數量。
// 存放 BeanDefinition 的緩存,key 爲 bean 的名稱,value 就是其 BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; // 提取 root,註冊 BeanDefinition (理論上 Spring 的配置文件,root 都應該是 beans 標籤) doRegisterBeanDefinitions(doc.getDocumentElement()); } 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); // 校驗 root 節點的命名空間是否爲默認的命名空間 (默認命名空間http://www.springframework.org/schema/beans) if (this.delegate.isDefaultNamespace(root)) { // 處理 profile 屬性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. // 校驗當前節點的 profile 是否符合當前環境定義的,若是不是則直接跳過,不解析該節點下的內容 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } // 解析前處理,留給子類實現 preProcessXml(root); // 解析註冊 BeanDefinition,見下文詳解 parseBeanDefinitions(root, this.delegate); // 解析後處理,留給子類實現 postProcessXml(root); this.delegate = parent; }
profile
主要是用於多環境開發,例如:
集成到 Web 環境時,在 web.xml 中加入如下代碼:
<coontext-param> <param-name>Spring.profiles.active</param-name> <param-value>dev</param-value> </coontext-param>
preProcessXml()
和 postProcessXml()
採用的 模板方法模式,子類能夠繼承 DefaultBeanDefinitionDoucumentReader
來重寫這兩個方法,這也是解析先後的擴展點。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 校驗 root 節點的命名空間是否爲默認的命名空間,這裏爲何再次效驗,由於調用解析前調用了preProcessXml() 方法,可能會對節點作修改 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)) { // 默認命名空間節點的處理,例如 <bean id="..." class="..."/> parseDefaultElement(ele, delegate); } else { // 自定義命名空間節點的處理,例如 <context:compoent-scan/>、<aop:aspectj-autoproxy> delegate.parseCustomElement(ele); } } } } else { // 自定義命名空間節點的處理 delegate.parseCustomElement(root); } } 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)) { doRegisterBeanDefinitions(ele); } }
上面 parseDefaultElement
方法中對 bean 標籤的處理方法 processBeanDefinition
最爲重要,下面來着重分析一下。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 將 ele 解析成 BeanDefinitionHolder,見下面詳解 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 若存在默認標籤下的子節點下再也不有自定義屬性,須要再次對自定義標籤再進行解析(基本不用,不作深刻分析) bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // 註冊最終的 BeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. // 發出響應事件,通知相關監聽器,這個 bean 已經註冊完 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
上面代碼主要步驟以下:
Element
解析成 BeanDefinitionHolder
。BeanDefinition
。ReaderEventListener#componentRegistered()
方法。能夠經過如下方式去註冊這個監聽器:public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { // 解析元素,封裝成 BeanDefinitionHolder return parseBeanDefinitionElement(ele, null); } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 獲取 id 屬性 String id = ele.getAttribute(ID_ATTRIBUTE); // 獲取 name 屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); // 將 name 屬性全部的名稱按照逗號或者分號(,;)分割成數組放入別名集合 aliases if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // beanName 默認使用 id String beanName = id; // 沒有指定 id 屬性 && 指定了 name 屬性 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { // 若是沒有指定id,beanName 等於第一個別名,剩下的依然做爲別名使用 beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { // 驗證 beanName 和 aliases 是否在同一個 <beans> 下已經存在 checkNameUniqueness(beanName, aliases, ele); } // 將元素解析成 GenericBeanDefinition AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { // 若是不存在 beanName 會根據 Spring 的命名規則生成一個 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.isTraceEnabled()) { logger.trace("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); // 用 beanDefinition 和 beanName 以及 aliasesArray 構建 BeanDefinitionHolder return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } 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 類型的 GenericBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 解析默認 bean 的各類屬性,見下方 parseBeanDefinitionAttributes 方法詳解 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // 提取 description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 解析元數據,見下方 parseMetaElements 方法詳解 parseMetaElements(ele, bd); // 解析 lookup-method 屬性,不多使用,不具體介紹 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 解析 replaced-method 屬性,不多使用,不具體介紹 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析 constructot-arg 屬性,見下方 parseConstructorArgElements 方法詳解 parseConstructorArgElements(ele, bd); // 解析 property 屬性,見下方 parsePropertyElements 方法詳解 parsePropertyElements(ele, bd); // 解析 qualifier 屬性,見下方 parseQualifierElements 方法詳解 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; }
上面代碼主要將 bean
標籤,解析爲 BeanDefinitionHolder
返回,主要步驟以下:
id
、name
屬性,將 name
按照 ,
或者 ;
分割做爲別名 (alias
)。GenericBeanDefinition
。
parseBeanDefinitionAttributes
方法解析 bean
標籤的全部屬性。parseMetaElements
方法解析元數據信息。parseLookupOverrideSubElements
方法解析 lookup-method
子標籤。parseReplacedMethodSubElements
方法解析 replaced-method
子標籤。parseConstructorArgElements
方法解析 constructor-arg
子標籤。parsePropertyElements
方法解析 property
子標籤。parseQualifierElements
方法解析 qualifier
子標籤。beanName
是否存在,不存在會根據 Spring 的命名規則生成一個。beanDefinition
、beanName
、aliasesArray
構建 BeanDefinitionHolder
返回。咱們這邊能夠簡單看一下 BeanDefinitionHolder
的屬性,以下:
public class BeanDefinitionHolder implements BeanMetadataElement { // bean 的定義元信息 private final BeanDefinition beanDefinition; // bean 的名稱 private final String beanName; // bean 的別名數組 @Nullable private final String[] aliases; ...省略其它代碼 }
BeanDefinitionHolder
其實就是對 BeanDefinition
的包裝。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { // 解析 singleton 屬性 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // singleton 屬性已經不支持了,使用了會直接拋出異常,請使用 scope 屬性替代 error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } // 解析 scope 屬性 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } // 解析 abstract 屬性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } // 解析 lazy 屬性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); // 若沒有設置或者設置成其餘字符都會被設置爲默認值 false if (isDefaultValue(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); // 解析 autowire 屬性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); // 解析 depends-on 屬性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } // 解析 autowire-candidate 屬性 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if (isDefaultValue(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } // 解析 primary 屬性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } // 解析 init-mehtod 屬性 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } // 若是 beans 標籤指定了 default-init-method 屬性,則會給全部此標籤下的 bean 都指定該 init-method else if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } // 解析 destory-method 屬性 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } // 若是 beans 標籤指定了 default-destory-method 屬性,則會給全部此標籤下的 bean 都指定該 destory-method else if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } // 解析 factory-method 屬性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } // 解析 factory-bean 屬性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
上面方法完成了對全部 bean
標籤屬性的解析。值得注意的地方是若是同時指定了 bean
標籤的 init-method
和 beans
標籤的 default-init-method
屬性,那麼優先使用前者,destory-mehtod
標籤也是同樣。
你們能夠去看一下
AbstractBeanDefinition
中定義的屬性就一目瞭然了,這裏限於篇幅緣由就不展現了。
這裏先回顧一下元數據 meta
屬性的使用。
這個屬性並不會體如今 user
的屬性當中,而是一個額外的聲明,當須要使用裏面的信息時能夠經過 BeanDefinition#getAttribute(key)
來獲取。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { // 獲取全部子節點 NodeList nl = ele.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // 提取 meta if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { Element metaElement = (Element) node; String key = metaElement.getAttribute(KEY_ATTRIBUTE); String value = metaElement.getAttribute(VALUE_ATTRIBUTE); // 使用 key、value 構造 BeanMetadataAttribute BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); // 記錄信息 attributeAccessor.addMetadataAttribute(attribute); } } }
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { // 獲取全部子節點 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // 提取 constructor-arg if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { // 解析 constructor-arg parseConstructorArgElement((Element) node, bd); } } } // <constructor-arg index="0" type="" value=""/> 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 類型 int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(index)); // 解析屬性值,見下面詳解 Object value = parsePropertyValue(ele, bd, null); // 使用 ConstructorArgumentValues.ValueHolder 類型來封裝解析出來的元素 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); // 若是 type 不爲空,設置 ValueHolder 的 type if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } // 若是 name 不爲空,設置 ValueHolder 的 name if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); // 判斷 index 是否重複,重複則拋出異常 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } // 將 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 當中 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 { try { // 這裏就是 constructor-arg 標籤中沒有指定 index 屬性 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)); // 將 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中 // 這裏和上面的 IndexedArgumentValue 相似,只不過上面是 Map,這裏是 List bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } }
上面代碼首先提取 constructor-arg
標籤中必要的屬性 (index
、type
、name
)。
index
屬性:
constructor-arg
的子元素。ConstructorArgumentsValues.ValueHolder
類型來封裝解析出來的元素。type
、name
和 index
屬性一併封裝在 ConstructorArgumentsValues.ValueHolder
類型中,並添加到當前 BeanDefinition
的 ConstructorArgumentValues
中的 LinkedHashMap
類型的屬性indexedArgumentValues
中。index
屬性:
constructor-arg
的子元素。ConstructorArgumentsValues.ValueHolder
類型來封裝解析出來的元素。type
、name
和 index
屬性一併封裝在 ConstructorArgumentsValues.ValueHolder
類型中,並添加到當前 BeanDefinition
的 ConstructorArgumentValues
中的 ArrayList
類型的屬性genericArgumentValues
中。public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"); // Should only have one child element: ref, value, list, etc. // 獲取全部子節點,例如 list、map等 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { 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; } } } // 提取 ref 屬性 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); // 提取 value 屬性 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); // 若是同時有 ref 和 value 屬性 || 有 ref 或 value 屬性同時又有子元素,拋出異常 if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } // 只有 ref 屬性,使用 RuntimeBeanReference 封裝對應的 ref 名稱 (該 ref 值指向另外一個 bean 的 beanName) // RuntimeBeanReference 起到佔位符的做用, ref 指向的 beanName 將在運行時被解析成真正的 bean 實例引用 if (hasRefAttribute) { 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; } // 只有 value 屬性,使用 TypedStringValue 封裝 else if (hasValueAttribute) { 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,直接拋出異常 error(elementName + " must specify a ref or value", ele); return null; } } public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) { return parsePropertySubElement(ele, bd, null); } public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { // 校驗是否爲默認的命名空間,若是不是則走解析自定義節點代碼 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } // 解析 bean 節點 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } // 解析 ref 節點 else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for <ref> element", ele); return null; } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } // 解析 idref 節點 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } // 解析 value 節點 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } // 解析 null 節點 else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } // 解析 array 節點 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } // 解析 list 節點 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } // 解析 set 節點 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } // 解析 map 節點 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } // 解析 props 節點 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } // 未知屬性,拋出異常 else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
從上面的代碼來看,對構造函數中屬性元素的解析,步驟以下:
description
或者 meta
。constructor-arg
上的 ref
和 value
屬性,以便於根據規則驗證正確性。其規則爲在 constructor-arg
上不存在一下狀況:
ref
和 value
屬性。ref
或者 value
屬性,而且又有子元素。public void parsePropertyElements(Element beanEle, BeanDefinition bd) { // 獲取全部子節點 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { // 解析 property 節點 parsePropertyElement((Element) node, bd); } } } // 這裏是解析 property 標籤,<property name="..." value="..."/> public void parsePropertyElement(Element ele, BeanDefinition bd) { // 獲取 name 屬性 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); // name 爲空,拋出異常 if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { // 出現兩個 name 相同的拋出異常 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } // 解析屬性值,跟構造器解析同樣,查看上方代碼 Object val = parsePropertyValue(ele, bd, propertyName); // 用 name 和 val 封裝成 PropertyValue PropertyValue pv = new PropertyValue(propertyName, val); // 解析元數據,跟 beans 標籤內的 meta 同樣 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); // 添加到 BeanDefiniton 的 PropertyValues 屬性中 bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
上面方法主要是遍歷 property
節點,而後解析屬性值封裝成 PropertyValue
添加到 BeanDefinition
的 PropertyValues
中。
注意:
property
節點相似於 POJO 中的set
方法,bean
中的屬性必需有set
方法不然會拋出異常。
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) { // 獲取子節點 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) { // 解析 qualifier 節點 parseQualifierElement((Element) node, bd); } } } public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) { // 提取 type String typeName = ele.getAttribute(TYPE_ATTRIBUTE); // type 爲空拋出異常 if (!StringUtils.hasLength(typeName)) { error("Tag 'qualifier' must have a 'type' attribute", ele); return; } this.parseState.push(new QualifierEntry(typeName)); try { AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName); qualifier.setSource(extractSource(ele)); // 提取 value String value = ele.getAttribute(VALUE_ATTRIBUTE); // value 不爲空,設置到 AutowireCandidateQualifier 的 attribute 中 if (StringUtils.hasLength(value)) { qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value); } // 獲取子節點 NodeList nl = ele.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // 若是是有 attribute 節點,進行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中 if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) { Element attributeEle = (Element) node; String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE); String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE); if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) { BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue); attribute.setSource(extractSource(attributeEle)); qualifier.addMetadataAttribute(attribute); } else { error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle); return; } } } // 設置 BeanDefinition 的 qualifier bd.addQualifier(qualifier); } finally { this.parseState.pop(); } }
對於 qualifier
元素的獲取,咱們大多數接觸的更可能是註解的形式,在使用 Spring 框架中進行自動注入時,Spring 容器中匹配的候選 Bean
必需有且只有一個。若是存在多個類型相同的 Bean
,且按照類型注入時,Spirng 容許經過 Qualifier
指定注入 Bean
的名稱,這樣歧義就消除了。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. // 獲取 beanName String beanName = definitionHolder.getBeanName(); // 以 key-value 的形式註冊,key 爲 beanName,value 爲 BeanDefinition。見下文詳解 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. // 註冊 bean 的別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { // 以 key-value 的形式註冊 bean 的別名,key 爲別名,value 爲 beanName。見下文詳解 registry.registerAlias(beanName, alias); } } } // DefaultListableBeanFactory.java 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 { // 驗證 Bean 的格式是否正確 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); // 這裏判斷 BeanDefinition 是否存在 if (existingDefinition != null) { // 這裏就是若是 Bean 定義以及存在,判斷是否能夠覆蓋,默認是能夠的 // Spring Boot 2.1開始這裏會手動設置 allowBeanDefinitionOverriding 的值爲 false if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("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.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } // 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // bean 是否已經開始建立 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中 this.beanDefinitionMap.put(beanName, beanDefinition); // 這裏將 beanDefinitionNames 寫時複製一份,相似於 CopyOnWriteArrayList List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // 從單例 Bean 註冊名稱列表中刪除當前 beanName removeManualSingletonName(beanName); } } // bean 不在建立狀態中 else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); // 由於 ConcurrentHashMap 是無序的,這裏將 beanName 放入 ArrayList,記錄註冊順序 this.beanDefinitionNames.add(beanName); // 從單例 Bean 註冊名稱列表中刪除當前 beanName removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } // 若是存在相同的 beanName 的 BeanDefinition,或者 beanName 已經存在單例對象,則將該 beanName 對應的緩存信息、單例對象清除,由於這些對象都是由老的 BeanDefinition 建立的,須要被覆蓋掉。再用新的 BeanDefinition 來建立這些緩存和單例對象 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } // SimpleAliasRegistry.java 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 相同,從緩存中移除 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; } // 若是不容許覆蓋,拋出異常 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } // 檢查 name 和 alias 是否存在循環引用。例如 A 的別名爲 B,B的別名爲A checkForAliasCircle(name, alias); // 將 alias 和 name 以 key-value 對放入到 aliasMap 中,進行緩存 this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }
上面代碼有兩個變量比較重要 beanDefinitionMap
和 beanDefinitionNames
,下面代碼是這兩個屬性在 DefaultListableBeanFactory
中的定義:
/** Map of bean definition objects, keyed by bean name. */ // 緩存 BeanDefinition 的 Map,key 爲 beanName,value 爲 BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); // 保存 BeanDefinition 的註冊順序,保存的是 beanName /** List of bean definition names, in registration order. */ private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
若是 BeanDefinition
是 AbstractBeanDefinition
類型,驗證 Bean
的格式是否正確。
此次效驗主要是對於
AbstractBeanDefinition
屬性中的methodOverrides
的校驗,校驗methodOverrides
是否與 工廠方法 並存或者methodOverrides
中的方法根本不存在。
判斷該 beanName
的 BeanDefinition
是否已經註冊過;若是存在判斷是否容許覆蓋,容許的話直接替換,不容許直接拋出異常。
默認的狀況下是容許的,可是在 Spring Boot 2.1 開始這裏會手動的設置爲不容許。
beanName
對應的 BeanDefinition
之前沒有註冊過,判斷 bean
是否已經開始建立;若是在建立中對 beanDefinitionMap
進行加鎖 (這裏雖然 beanDefinitionMap
是線程安全的 ConcurrentHashMap
,單個操做是線程安全的但多個操做不是,因此這裏手動加鎖),而後將 beanName
和 BeanDefinition
以 key-value
形式放入 beanDefinitionMap
緩存中,而後寫時複製一份 beanDefinitionNames
,將 beaName
緩存進去,記錄 bean
的註冊順序;若是不在建立直接將 BeanDefinition
和 beanName
分別放入 beanDefinitionMap
和 beanDefinitionNames
中。
最後判斷若是 BeanDefinition
已經註冊過,或者 beanName
已經存在單例對象,則將該 beanName
對應的緩存信息、單例對象清除,由於這些對象都是由老的 BeanDefinition
建立的,須要被覆蓋掉。再用新的 BeanDefinition
來建立這些緩存和單例對象。
本文主要介紹了經過 XML 文件的方式註冊 Bean
,咱們能夠從新梳理一下思路:
解析 XML 文件,構建成 AbstractBeanDefinition (GenericBeanDefinition)
對象來存放全部解析出來的屬性。
將 AbstractBeanDefinition
、beanName
、aliasesArray
構建成 BeanDefinitionHolder
對象。
最後經過 BeanDefinitionHolder
將 beanName
和 BeanDefinition
註冊到 DefaultListableBeanFactory
中,也就是保存起來。
上文提到的兩個比較重要的屬性
beanDefinitionNames
和beanDefinitionMap
,在後面都會屢次用到,能夠重點關注一下。
最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新,如今是 0.0.1
版本。地址:https://github.com/leisurexi/tiny-spring。訪問新博客地址,觀看效果更佳 https://leisurexi.github.io/
《Spring 源碼深度解析》—— 郝佳