Spring XML Bean 定義的加載和注冊

前言

本篇文章主要介紹 Spring IoC 容器怎么加載?bean?的定義元信息您没。

下圖是一個大致的流程圖:

第一次畫圖葫慎,畫的有點爛墅拭。:joy:

正文

首先定義一個簡單的 POJO钦椭,如下:

publicclassUser{privateLongid;privateString name;publicLonggetId() {returnid;}publicvoid setId(Longid) {this.id = id;}publicString getName() {returnname;}publicvoid setName(String name) {this.name = name;}@OverridepublicString toString() {return"User{"+"id="+ id +", name='"+ name +'\''+'}';}}

再編寫一個 XML 文件谬晕。

<?xml version="1.0"encoding="UTF-8"?>

最后再來一個測試類盈简。

@Testpublicvoidtest(){DefaultListableBeanFactory beanFactory =newDefaultListableBeanFactory();XmlBeanDefinitionReader reader =newXmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("META-INF/spring-bean.xml");User user = beanFactory.getBean("user", User.class);System.out.println(user);}

上面這段代碼比較簡單凑耻,無非就是聲明?bean?工廠,然后通過指定的 XML 文件加載?bean?的定義元信息柠贤,最后通過?bean?工廠獲取?bean?香浩。

DefaultListableBeanFactory

首先我們來了解一下?DefaultListableBeanFactory?,下面是該類的類圖及層次結(jié)構(gòu)臼勉。

AliasRegistry:?定義對?alias?的簡單增刪改等操作邻吭。

SimpleAliasRegistry:?主要使用?map?作為?alias?的緩存,并對接口?AliasRegistry?進(jìn)行實現(xiàn)坚俗。

SingletonBeanRegistry:?定義了對單例 bean 的注冊及獲取镜盯。

BeanFactory:?定義獲取單個?bean?及?bean?的各種屬性。

DefaultSingletonBeanRegistry:?對接口?SingletonBeanRegistry?各函數(shù)的實現(xiàn)猖败。

HierarchicalBeanFactory:?繼承?BeanFactory?速缆,也就是在?BeanFactory?定義的功能的基礎(chǔ)上增加了對?parentBeanFactory?的支持。

BeanDefinitionRegistry:?定義了對?BeanDefinition?的各種增刪改操作恩闻。

FactoryBeanRegistrySupport:?在?DefaultSingletonBeanRegistry?基礎(chǔ)上增加了對?FactoryBean?的特殊處理功能艺糜。

ConfigurableBeanFactory:?提供配置?BeanFactory?的各種方法。

ListableBeanFactory:?繼承?BeanFactory?提供了獲取多個?bean?的各種方法幢尚。

AbstractBeanFactory:?綜合?FactoryBeanRegistrySupport?和?ConfigurableBeanFactory?的功能破停。

AutowireCapableBeanFactory:?提供創(chuàng)建?bean?、自動注入尉剩、初始化以及應(yīng)用?bean?的后處理器黑界。

AbstractAutowireCapableBeanFactory:?綜合?AbstractBeanFactory?并對接口?AutowireCapableBeanFactory?進(jìn)行實現(xiàn)。

ConfigurableListableBeanFactory:?BeanFactory?配置清單朗鸠,指定忽略類型及接口等。

DefaultListableBeanFactory:?綜合上面所有功能胎挎,主要是對?bean?注冊后的處理。

可以看到上面的接口大多數(shù)是定義了一些功能或在父接口上擴展了一些功能忆家,?DefaultListableBeanFactory?實現(xiàn)了所有接口弦赖,大多數(shù)默認(rèn)情況下我們所使用的?beanFactory?就是?DefaultListableBeanFactory?蹬竖。

1.AbstractBeanDefinitionReader#loadBeanDefinitions 方法

publicintloadBeanDefinitions(String location, @Nullable Set actualResources)throwsBeanDefinitionStoreException {// 獲取 resourceLoader币厕,這邊是 PathMatchingResourcePatternResolverResourceLoader resourceLoader = getResourceLoader();if(resourceLoader ==null) {thrownewBeanDefinitionStoreException("Cannot load bean definitions from location ["+ location +"]: no ResourceLoader available");}// 判斷 resourceLoader 是否是 ResourcePatternResolver旦装,我們這邊是符合的if(resourceLoaderinstanceofResourcePatternResolver) {// Resource pattern matching available.try{// 根據(jù)路徑獲取所欲符合的配置文件并封裝成 Resource 對象Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);// 根據(jù) Resource 加載 bean definition,并返回數(shù)量呻袭,見下面詳解intcount= loadBeanDefinitions(resources);if(actualResources !=null) {Collections.addAll(actualResources, resources);}if(logger.isTraceEnabled()) {logger.trace("Loaded "+count+" bean definitions from location pattern ["+ location +"]");}returncount;}catch(IOException ex) {thrownewBeanDefinitionStoreException("Could not resolve bean definition resource pattern ["+ location +"]", ex);}}else{// Can only load single resources by absolute URL.// 只能通過絕對路徑加載單個資源Resource resource = resourceLoader.getResource(location);// 根據(jù) Resource 加載 bean definition,并返回數(shù)量页响,見下面詳解intcount= loadBeanDefinitions(resource);if(actualResources !=null) {actualResources.add(resource);}if(logger.isTraceEnabled()) {logger.trace("Loaded "+count+" bean definitions from location ["+ location +"]");}returncount;}}

上面方法主要是將資源文件轉(zhuǎn)換為?Resource?對象栈拖,然后調(diào)用?loadBeanDefinitions(Resource...)?加載?BeanDefinition?涩哟。

publicintloadBeanDefinitions(Resource... resources)throwsBeanDefinitionStoreException {Assert.notNull(resources,"Resource array must not be null");intcount=0;for(Resource resource : resources) {// 加載 BeanDefinition染簇,見下文詳解count+= loadBeanDefinitions(resource);}returncount;}

該方法主要就是遍歷?resources?然后調(diào)用?XmlBeanDefinitionReader#loadBeanDefinitions(Resource)?砾赔。

2.XmlBeanDefinitionReader#loadBeanDefinitions

publicintloadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException{// 將 Resource 封裝成 EncodedResource暴心,也就是對資源指定編碼和字符集returnloadBeanDefinitions(newEncodedResource(resource));}publicintloadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException{Assert.notNull(encodedResource,"EncodedResource must not be null");if(logger.isTraceEnabled()) {logger.trace("Loading XML bean definitions from "+ encodedResource);}// 當(dāng)前正在加載的 EncodedResourceSet currentResources =this.resourcesCurrentlyBeingLoaded.get();if(currentResources ==null) {currentResources =newHashSet<>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}// 如果當(dāng)前 EncodedResource 以及存在,代表出現(xiàn)了循環(huán)加載檀夹,拋出異常if(!currentResources.add(encodedResource)) {thrownewBeanDefinitionStoreException("Detected cyclic loading of "+ encodedResource +" - check your import definitions!");}try{// 獲取 Resource 的輸入流InputStream inputStream = encodedResource.getResource().getInputStream();try{// 將 inputStream 封裝成 org.xml.sax.InputSourceInputSource inputSource =newInputSource(inputStream);// 如果 encodedResource 的編碼不為空炸渡,設(shè)置 inputSource 的編碼if(encodedResource.getEncoding() !=null) {inputSource.setEncoding(encodedResource.getEncoding());}// 加載 BeanDefinition (方法以 do 開頭,真正處理的方法)returndoLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally{// 關(guān)閉流inputStream.close();}}catch(IOException ex) {thrownewBeanDefinitionStoreException("IOException parsing XML document from "+ encodedResource.getResource(), ex);}finally{// 當(dāng)前資源以及加載完畢吼畏,從 currentResources 中移除currentResources.remove(encodedResource);if(currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}

上面主要將?Resource?封裝成?EncodedResource?,也就是制定資源的編碼和字符集藕夫。然后獲取?Resource?的輸入流?InputStream?毅贮,并封裝成?InputSource?設(shè)置其編碼滩褥,最終調(diào)用?doLoadBeanDefinitions?開始真正的加載流程铺然。

3.XmlBeanDefinitionReader#doLoadBeanDefinitions

protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource)throwsBeanDefinitionStoreException {try{// 根據(jù) inputSource 和 resource 加載 XML 文件魄健,并封裝成 Document,見下文詳解Document doc = doLoadDocument(inputSource, resource);// 用 doc 去解析和注冊 bean definition析恋,見下文詳解intcount= registerBeanDefinitions(doc, resource);if(logger.isDebugEnabled()) {logger.debug("Loaded "+count+" bean definitions from "+ resource);}returncount;}catch(BeanDefinitionStoreException ex) {throwex;}catch(SAXParseException ex) {thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"Line "+ ex.getLineNumber() +" in XML document from "+ resource +" is invalid", ex);}catch(SAXException ex) {thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"XML document from "+ resource +" is invalid", ex);}catch(ParserConfigurationException ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from "+ resource, ex);}catch(IOException ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from "+ resource, ex);}catch(Throwable ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from "+ resource, ex);}}

上面代碼拋開異常處理,邏輯非常簡單并村,就是用?inputSource?和?resource?加載 XML 文件橘霎,并封裝成?Document?對象瓦盛,然后去注冊?BeanDefinition?原环。

4.XmlBeanDefinitionReader#doLoadDocument

protectedDocumentdoLoadDocument(InputSource inputSource, Resource resource)throwsException{returnthis.documentLoader.loadDocument(inputSource, getEntityResolver(),this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());}// 獲取 XML 文件的驗證模式protectedintgetValidationModeForResource(Resource resource){// 如果手動指定了驗證模式則使用指定的驗證模式intvalidationModeToUse = getValidationMode();if(validationModeToUse != VALIDATION_AUTO) {returnvalidationModeToUse;}// 如果未指定則使用自動檢測玄组,其實就是判斷文件是否包含 DOCTYPE俄讹,如果intdetectedMode = detectValidationMode(resource);if(detectedMode != VALIDATION_AUTO) {returndetectedMode;}// 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).// 如果沒有找到驗證患膛,默認(rèn)使用 XSD 模式胞此,因為 DTD 已經(jīng)不維護(hù)了returnVALIDATION_XSD;}// DefaultDocumentLoader.java// 這里就是使用 DocumentLoader 去加載 XML 文件漱牵。首先創(chuàng)建 DocumentBuilderFactory,再通過 DocumentBuilderFactory 創(chuàng)建 DocumentBuilder灵临,進(jìn)而解析 inputSource 來返回 Document 對象publicDocumentloadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler,intvalidationMode,booleannamespaceAware)throwsException{DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);if(logger.isTraceEnabled()) {logger.trace("Using JAXP provider ["+ factory.getClass().getName() +"]");}DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);returnbuilder.parse(inputSource);}

detectValidationMode()?方法其實就是讀取文件內(nèi)容,判斷是否包含?DOCTYPE?顿涣,如果包含就是 DTD 否則就是 XSD涛碑。

獲取 XML 配置文件的驗證模式。XML 文件的驗證模式是用來保證 XML 文件的正確性揉阎,常見的驗證模式有 DTD 和 XSD毙籽。

DTD XML 格式示例:

STD XML 格式示例:

5.XmlBeanDefinitionReader#registerBeanDefinitions

publicintregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{// 獲取 DefaultBeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// 獲取注冊中心么抗,再靠注冊中心獲取注冊之前以及注冊過的 BeanDefinition 數(shù)量intcountBefore = getRegistry().getBeanDefinitionCount();// 解析并注冊 BeanDefinition乖坠,見下文詳情documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 獲取注冊過后 BeanDefinition 數(shù)量減去注冊之前的數(shù)量仰迁,得到的就是本次注冊的數(shù)量returngetRegistry().getBeanDefinitionCount() - countBefore;}

這里的?getRegistry()?方法返回的就是?DefaultListableBeanFactory?徐许,因為就只有它實現(xiàn)了?BeanDefinitionRegistry?接口翻默。

DefaultListableBeanFactory?中定義了存放?BeanDefinition?的緩存修械,所以?getBeanDefinitionCount()?方法返回的就是?beanDefinitionMap?的數(shù)量。

// 存放 BeanDefinition 的緩存蹦渣,key 為 bean 的名稱柬唯,value 就是其 BeanDefinitionprivatefinalMap beanDefinitionMap =newConcurrentHashMap<>(256);

6.DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions

publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){this.readerContext = readerContext;// 提取 root,注冊 BeanDefinition (理論上 Spring 的配置文件斟薇,root 都應(yīng)該是 beans 標(biāo)簽)doRegisterBeanDefinitions(doc.getDocumentElement());}protectedvoiddoRegisterBeanDefinitions(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 節(jié)點的命名空間是否為默認(rèn)的命名空間 (默認(rèn)命名空間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.// 校驗當(dāng)前節(jié)點的 profile 是否符合當(dāng)前環(huán)境定義的蕊温,如果不是則直接跳過义矛,不解析該節(jié)點下的內(nèi)容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;}}}// 解析前處理,留給子類實現(xiàn)preProcessXml(root);// 解析注冊 BeanDefinition制轰,見下文詳解parseBeanDefinitions(root,this.delegate);// 解析后處理男杈,留給子類實現(xiàn)postProcessXml(root);this.delegate= parent;}

profile?主要是用于多環(huán)境開發(fā)伶棒,例如:

集成到 Web 環(huán)境時,在 web.xml 中加入以下代碼:

Spring.profiles.activedev

preProcessXml()?和?postProcessXml()?采用的?模板方法模式?宛渐,子類可以繼承?DefaultBeanDefinitionDoucumentReader?來重寫這兩個方法,這也是解析前后的擴展點鳍烁。

7.DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions

protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegatedelegate){// 校驗 root 節(jié)點的命名空間是否為默認(rèn)的命名空間,這里為什么再次效驗爹梁,因為調(diào)用解析前調(diào)用了preProcessXml() 方法,可能會對節(jié)點做修改if(delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);if(node instanceof Element) {Element ele = (Element) node;if(delegate.isDefaultNamespace(ele)) {// 默認(rèn)命名空間節(jié)點的處理积糯,例如 <bean id="..." class="..."/>parseDefaultElement(ele,delegate);}else{// 自定義命名空間節(jié)點的處理,例如 <context:compoent-scan/>川慌、<aop:aspectj-autoproxy>delegate.parseCustomElement(ele);}}}}else{// 自定義命名空間節(jié)點的處理delegate.parseCustomElement(root);}}privatevoidparseDefaultElement(Element ele, BeanDefinitionParserDelegatedelegate){// 對 import 標(biāo)簽的處理if(delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}// 對 alias 標(biāo)簽的處理elseif(delegate.nodeNameEquals(ele, ALIAS_ELEMENT)){processAliasRegistration(ele);}// 對 bean 標(biāo)簽的處理elseif(delegate.nodeNameEquals(ele, BEAN_ELEMENT)){processBeanDefinition(ele,delegate);}// 對 beans 標(biāo)簽的處理elseif(delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)){doRegisterBeanDefinitions(ele);}}

上面?parseDefaultElement?方法中對 bean 標(biāo)簽的處理方法?processBeanDefinition?最為重要兑燥,下面來著重分析一下。

7-1.DefaultBeanDefinitionDocumentReader#processBeanDefinition

protectedvoidprocessBeanDefinition(Element ele, BeanDefinitionParserDelegatedelegate){// 將 ele 解析成 BeanDefinitionHolder力崇,見下面詳解BeanDefinitionHolder bdHolder =delegate.parseBeanDefinitionElement(ele);if(bdHolder !=null) {// 若存在默認(rèn)標(biāo)簽下的子節(jié)點下不再有自定義屬性,需要再次對自定義標(biāo)簽再進(jìn)行解析(基本不用茧吊,不做深入分析)bdHolder =delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try{// Register the final decorated instance.// 注冊最終的 BeanDefinitionBeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch(BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '"+bdHolder.getBeanName() +"'", ele, ex);}// Send registration event.// 發(fā)出響應(yīng)事件,通知相關(guān)監(jiān)聽器,這個 bean 已經(jīng)注冊完getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));}}

上面代碼主要步驟如下:

將?Element?解析成?BeanDefinitionHolder?乳讥。

若存在默認(rèn)標(biāo)簽下的子節(jié)點下有自定義屬性,需要再次對自定義標(biāo)簽再進(jìn)行解析汹忠。

注冊?BeanDefinition?错维。

發(fā)出響應(yīng)事件参歹,通知相關(guān)監(jiān)聽器僧界,這個 bean 已經(jīng)注冊完捂襟,具體詳情可以查看?ReaderEventListener#componentRegistered()?方法涨共。可以通過以下方式去注冊這個監(jiān)聽器:

7-1-1.BeanDefinitionParseDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Elementele) {// 解析元素,封裝成 BeanDefinitionHolderreturnparseBeanDefinitionElement(ele,null);}public BeanDefinitionHolder parseBeanDefinitionElement(Elementele,@NullableBeanDefinition containingBean) {// 獲取 id 屬性Stringid = ele.getAttribute(ID_ATTRIBUTE);// 獲取 name 屬性StringnameAttr = ele.getAttribute(NAME_ATTRIBUTE);List aliases =newArrayList<>();// 將 name 屬性所有的名稱按照逗號或者分號(,;)分割成數(shù)組放入別名集合 aliasesif(StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}// beanName 默認(rèn)使用 idStringbeanName = 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> 下已經(jīng)存在checkNameUniqueness(beanName, aliases, ele);}// 將元素解析成 GenericBeanDefinitionAbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if(beanDefinition !=null) {// 如果不存在 beanName 會根據(jù) Spring 的命名規(guī)則生成一個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.StringbeanClassName = 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);returnnull;}}String[] aliasesArray = StringUtils.toStringArray(aliases);// 用 beanDefinition 和 beanName 以及 aliasesArray 構(gòu)建 BeanDefinitionHolderreturnnewBeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}returnnull;}public AbstractBeanDefinition parseBeanDefinitionElement(Elementele,StringbeanName,@NullableBeanDefinition containingBean) {this.parseState.push(newBeanEntry(beanName));StringclassName =null;// 獲取 class 屬性if(ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}Stringparent =null;// 獲取 parent 屬性if(ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}try{// 創(chuàng)建用于承載屬性的 AbstractBeanDefinition 類型的 GenericBeanDefinitionAbstractBeanDefinition bd = createBeanDefinition(className, parent);// 解析默認(rèn) bean 的各種屬性趁窃,見下方 parseBeanDefinitionAttributes 方法詳解parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);// 提取 descriptionbd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));// 解析元數(shù)據(jù)瀑构,見下方 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));returnbd;}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();}returnnull;}

上面代碼主要將?bean?標(biāo)簽金砍,解析為?BeanDefinitionHolder?返回恕稠,主要步驟如下:

解析?id?千扶、?name?屬性,將?name?按照?,?或者?;?分割作為別名 (?alias?)妆绞。

解析剩下的屬性,并封裝成?GenericBeanDefinition?图焰。

調(diào)用?parseBeanDefinitionAttributes?方法解析?bean?標(biāo)簽的所有屬性驰徊。

調(diào)用?parseMetaElements?方法解析元數(shù)據(jù)信息堕阔。

調(diào)用?parseLookupOverrideSubElements?方法解析?lookup-method?子標(biāo)簽。

調(diào)用?parseReplacedMethodSubElements?方法解析?replaced-method?子標(biāo)簽颗味。

調(diào)用?parseConstructorArgElements?方法解析?constructor-arg?子標(biāo)簽超陆。

調(diào)用?parsePropertyElements?方法解析?property?子標(biāo)簽。

調(diào)用?parseQualifierElements?方法解析?qualifier?子標(biāo)簽浦马。

判斷?beanName?是否存在,不存在會根據(jù) Spring 的命名規(guī)則生成一個晶默。

使用?beanDefinition?谨娜、?beanName?、?aliasesArray?構(gòu)建?BeanDefinitionHolder?返回磺陡。

我們這邊可以簡單看一下?BeanDefinitionHolder?的屬性趴梢,如下:

publicclassBeanDefinitionHolderimplementsBeanMetadataElement{// bean 的定義元信息privatefinalBeanDefinition beanDefinition;// bean 的名稱privatefinalString beanName;// bean 的別名數(shù)組@NullableprivatefinalString[] aliases;? ? ...省略其它代碼}

BeanDefinitionHolder?其實就是對?BeanDefinition?的包裝。

parseBeanDefinitionAttributes

public AbstractBeanDefinition parseBeanDefinitionAttributes(Elementele,StringbeanName,@NullableBeanDefinition containingBean, AbstractBeanDefinition bd) {// 解析 singleton 屬性if(ele.hasAttribute(SINGLETON_ATTRIBUTE)) {// singleton 屬性已經(jīng)不支持了币他,使用了會直接拋出異常坞靶,請使用 scope 屬性替代error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);}// 解析 scope 屬性elseif(ele.hasAttribute(SCOPE_ATTRIBUTE)) {bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));}elseif(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 屬性StringlazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);// 若沒有設(shè)置或者設(shè)置成其他字符都會被設(shè)置為默認(rèn)值 falseif(isDefaultValue(lazyInit)) {lazyInit =this.defaults.getLazyInit();}bd.setLazyInit(TRUE_VALUE.equals(lazyInit));// 解析 autowire 屬性Stringautowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);bd.setAutowireMode(getAutowireMode(autowire));// 解析 depends-on 屬性if(ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {StringdependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));}// 解析 autowire-candidate 屬性StringautowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);if(isDefaultValue(autowireCandidate)) {StringcandidatePattern =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)) {StringinitMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);bd.setInitMethodName(initMethodName);}// 如果 beans 標(biāo)簽指定了 default-init-method 屬性,則會給所有此標(biāo)簽下的 bean 都指定該 init-methodelseif(this.defaults.getInitMethod() !=null) {bd.setInitMethodName(this.defaults.getInitMethod());bd.setEnforceInitMethod(false);}// 解析 destory-method 屬性if(ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {StringdestroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);bd.setDestroyMethodName(destroyMethodName);}// 如果 beans 標(biāo)簽指定了 default-destory-method 屬性蝴悉,則會給所有此標(biāo)簽下的 bean 都指定該 destory-methodelseif(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));}returnbd;}

上面方法完成了對所有?bean?標(biāo)簽屬性的解析彰阴。值得注意的地方是如果同時指定了?bean?標(biāo)簽的?init-method?和?beans?標(biāo)簽的?default-init-method?屬性,那么優(yōu)先使用前者拍冠,?destory-mehtod?標(biāo)簽也是一樣尿这。

大家可以去看一下?AbstractBeanDefinition?中定義的屬性就一目了然了,這里限于篇幅原因就不展示了庆杜。

parseMetaElements

這里先回顧一下元數(shù)據(jù)?meta?屬性的使用射众。

這個屬性并不會體現(xiàn)在?user?的屬性當(dāng)中,而是一個額外的聲明欣福,當(dāng)需要使用里面的信息時可以通過?BeanDefinition#getAttribute(key)?來獲取责球。

publicvoidparseMetaElements(Elementele, BeanMetadataAttributeAccessor attributeAccessor) {// 獲取所有子節(jié)點NodeList nl = ele.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);// 提取 meta if(isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {ElementmetaElement = (Element) node;Stringkey = metaElement.getAttribute(KEY_ATTRIBUTE);Stringvalue = metaElement.getAttribute(VALUE_ATTRIBUTE);// 使用 key、value 構(gòu)造 BeanMetadataAttributeBeanMetadataAttribute attribute =newBeanMetadataAttribute(key, value);attribute.setSource(extractSource(metaElement));// 記錄信息attributeAccessor.addMetadataAttribute(attribute);}}}

parseConstructorArgElements

publicvoidparseConstructorArgElements(ElementbeanEle, BeanDefinition bd) {// 獲取所有子節(jié)點NodeList nl = beanEle.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);// 提取 constructor-argif(isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {// 解析 constructor-argparseConstructorArgElement((Element) node, bd);}}}// <constructor-arg index="0" type="" value=""/>publicvoidparseConstructorArgElement(Elementele, BeanDefinition bd) {// 提取 index 屬性StringindexAttr = ele.getAttribute(INDEX_ATTRIBUTE);// 提取 type 屬性StringtypeAttr = ele.getAttribute(TYPE_ATTRIBUTE);// 提取 name 屬性StringnameAttr = ele.getAttribute(NAME_ATTRIBUTE);// index 不為空if(StringUtils.hasLength(indexAttr)) {try{// 轉(zhuǎn)換為 int 類型intindex = Integer.parseInt(indexAttr);if(index <0) {error("'index' cannot be lower than 0", ele);}else{try{this.parseState.push(newConstructorArgumentEntry(index));// 解析屬性值拓劝,見下面詳解Objectvalue = parsePropertyValue(ele, bd,null);// 使用 ConstructorArgumentValues.ValueHolder 類型來封裝解析出來的元素ConstructorArgumentValues.ValueHolder valueHolder =newConstructorArgumentValues.ValueHolder(value);// 如果 type 不為空雏逾,設(shè)置 ValueHolder 的 typeif(StringUtils.hasLength(typeAttr)) {valueHolder.setType(typeAttr);}// 如果 name 不為空,設(shè)置 ValueHolder 的 nameif(StringUtils.hasLength(nameAttr)) {valueHolder.setName(nameAttr);}valueHolder.setSource(extractSource(ele));// 判斷 index 是否重復(fù)郑临,重復(fù)則拋出異常if(bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {error("Ambiguous constructor-arg entries for index "+ index, ele);}// 將 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 當(dāng)中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 標(biāo)簽中沒有指定 index 屬性this.parseState.push(newConstructorArgumentEntry());Objectvalue = parsePropertyValue(ele, bd,null);ConstructorArgumentValues.ValueHolder valueHolder =newConstructorArgumentValues.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,這里是 Listbd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);}finally{this.parseState.pop();}}}

上面代碼首先提取?constructor-arg?標(biāo)簽中必要的屬性 (?index?厢洞、?type?仇让、?name?)典奉。

如果指定了?index?屬性:

解析?constructor-arg?的子元素。

使用?ConstructorArgumentsValues.ValueHolder?類型來封裝解析出來的元素丧叽。

將?type?卫玖、?name?和?index?屬性一并封裝在?ConstructorArgumentsValues.ValueHolder?類型中,并添加到當(dāng)前?BeanDefinition?的?ConstructorArgumentValues?中的?LinkedHashMap?類型的屬性?indexedArgumentValues?中踊淳。

如果有指定?index?屬性:

解析?constructor-arg?的子元素假瞬。

使用?ConstructorArgumentsValues.ValueHolder?類型來封裝解析出來的元素。

將?type?迂尝、?name?和?index?屬性一并封裝在?ConstructorArgumentsValues.ValueHolder?類型中脱茉,并添加到當(dāng)前?BeanDefinition?的?ConstructorArgumentValues?中的?ArrayList?類型的屬性?genericArgumentValues?中。

parsePropertyValue

publicObject parsePropertyValue(Element ele, BeanDefinition bd,@NullableString propertyName) {String elementName = (propertyName !=null?"<property> element for property '"+ propertyName +"'":"<constructor-arg> element");// Should only have one child element: ref, value, list, etc.// 獲取所有子節(jié)點垄开,例如 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 封裝對應(yīng)的 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));returnref;}// 只有 value 屬性,使用 TypedStringValue 封裝elseif(hasValueAttribute) {TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));valueHolder.setSource(extractSource(ele));returnvalueHolder;}elseif(subElement !=null) {// 解析子元素returnparsePropertySubElement(subElement, bd);}else{// Neither child element nor "ref" or "value" attribute found.// 沒有子元素签财,也沒有 ref 和 value串慰,直接拋出異常error(elementName +" must specify a ref or value", ele);returnnull;}}publicObject parsePropertySubElement(Element ele,@NullableBeanDefinition bd) {returnparsePropertySubElement(ele, bd,null);}publicObject parsePropertySubElement(Element ele,@NullableBeanDefinition bd,@NullableString defaultValueType) {// 校驗是否為默認(rèn)的命名空間,如果不是則走解析自定義節(jié)點代碼if(!isDefaultNamespace(ele)) {returnparseNestedCustomElement(ele, bd);}// 解析 bean 節(jié)點elseif(nodeNameEquals(ele, BEAN_ELEMENT)) {BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);if(nestedBd !=null) {nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);}returnnestedBd;}// 解析 ref 節(jié)點elseif(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);returnnull;}}if(!StringUtils.hasText(refName)) {error("<ref> element contains empty target attribute", ele);returnnull;}RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);ref.setSource(extractSource(ele));returnref;}// 解析 idref 節(jié)點elseif(nodeNameEquals(ele, IDREF_ELEMENT)) {returnparseIdRefElement(ele);}// 解析 value 節(jié)點elseif(nodeNameEquals(ele, VALUE_ELEMENT)) {returnparseValueElement(ele, defaultValueType);}// 解析 null 節(jié)點elseif(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));returnnullHolder;}// 解析 array 節(jié)點elseif(nodeNameEquals(ele, ARRAY_ELEMENT)) {returnparseArrayElement(ele, bd);}// 解析 list 節(jié)點elseif(nodeNameEquals(ele, LIST_ELEMENT)) {returnparseListElement(ele, bd);}// 解析 set 節(jié)點elseif(nodeNameEquals(ele, SET_ELEMENT)) {returnparseSetElement(ele, bd);}// 解析 map 節(jié)點elseif(nodeNameEquals(ele, MAP_ELEMENT)) {returnparseMapElement(ele, bd);}// 解析 props 節(jié)點elseif(nodeNameEquals(ele, PROPS_ELEMENT)) {returnparsePropsElement(ele);}// 未知屬性唱蒸,拋出異常else{error("Unknown property sub-element: ["+ ele.getNodeName() +"]", ele);returnnull;}}

從上面的代碼來看邦鲫,對構(gòu)造函數(shù)中屬性元素的解析,步驟如下:

略過?description?或者?meta?神汹。

提取?constructor-arg?上的?ref?和?value?屬性庆捺,以便于根據(jù)規(guī)則驗證正確性。其規(guī)則為在?constructor-arg?上不存在一下情況:

同時存在?ref?和?value?屬性屁魏。

存在?ref?或者?value?屬性滔以,并且又有子元素。

parsePropertyElements

publicvoidparsePropertyElements(ElementbeanEle, BeanDefinition bd) {// 獲取所有子節(jié)點NodeList nl = beanEle.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);if(isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {// 解析 property 節(jié)點parsePropertyElement((Element) node, bd);}}}// 這里是解析 property 標(biāo)簽氓拼,<property name="..." value="..."/>publicvoidparsePropertyElement(Elementele, BeanDefinition bd) {// 獲取 name 屬性StringpropertyName = ele.getAttribute(NAME_ATTRIBUTE);// name 為空你画,拋出異常if(!StringUtils.hasLength(propertyName)) {error("Tag 'property' must have a 'name' attribute", ele);return;}this.parseState.push(newPropertyEntry(propertyName));try{// 出現(xiàn)兩個 name 相同的拋出異常if(bd.getPropertyValues().contains(propertyName)) {error("Multiple 'property' definitions for property '"+ propertyName +"'", ele);return;}// 解析屬性值,跟構(gòu)造器解析一樣桃漾,查看上方代碼Objectval = parsePropertyValue(ele, bd, propertyName);// 用 name 和 val 封裝成 PropertyValuePropertyValue pv =newPropertyValue(propertyName, val);// 解析元數(shù)據(jù)坏匪,跟 beans 標(biāo)簽內(nèi)的 meta 一樣parseMetaElements(ele, pv);pv.setSource(extractSource(ele));// 添加到 BeanDefiniton 的 PropertyValues 屬性中bd.getPropertyValues().addPropertyValue(pv);}finally{this.parseState.pop();}}

上面方法主要是遍歷?property?節(jié)點,然后解析屬性值封裝成?PropertyValue?添加到?BeanDefinition?的?PropertyValues?中撬统。

注意:?property?節(jié)點類似于 POJO 中的?set?方法适滓,?bean?中的屬性必需有?set?方法否則會拋出異常。

parseQualifierElements

publicvoidparseQualifierElements(ElementbeanEle, AbstractBeanDefinition bd) {// 獲取子節(jié)點NodeList nl = beanEle.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);if(isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {// 解析 qualifier 節(jié)點parseQualifierElement((Element) node, bd);}}}publicvoidparseQualifierElement(Elementele, AbstractBeanDefinition bd) {// 提取 typeStringtypeName = ele.getAttribute(TYPE_ATTRIBUTE);// type 為空拋出異常if(!StringUtils.hasLength(typeName)) {error("Tag 'qualifier' must have a 'type' attribute", ele);return;}this.parseState.push(newQualifierEntry(typeName));try{AutowireCandidateQualifier qualifier =newAutowireCandidateQualifier(typeName);qualifier.setSource(extractSource(ele));// 提取 valueStringvalue = ele.getAttribute(VALUE_ATTRIBUTE);// value 不為空恋追,設(shè)置到 AutowireCandidateQualifier 的 attribute 中if(StringUtils.hasLength(value)) {qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);}// 獲取子節(jié)點NodeList nl = ele.getChildNodes();for(inti =0; i < nl.getLength(); i++) {Node node = nl.item(i);// 如果是有 attribute 節(jié)點凭迹,進(jìn)行解析罚屋,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中if(isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {ElementattributeEle = (Element) node;StringattributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);StringattributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);if(StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {BeanMetadataAttribute attribute =newBeanMetadataAttribute(attributeName, attributeValue);attribute.setSource(extractSource(attributeEle));qualifier.addMetadataAttribute(attribute);}else{error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);return;}}}// 設(shè)置 BeanDefinition 的 qualifierbd.addQualifier(qualifier);}finally{this.parseState.pop();}}

對于?qualifier?元素的獲取,我們大多數(shù)接觸的更多是注解的形式嗅绸,在使用 Spring 框架中進(jìn)行自動注入時脾猛,Spring 容器中匹配的候選?Bean?必需有且只有一個。如果存在多個類型相同的?Bean?朽砰,且按照類型注入時尖滚,Spirng 允許通過?Qualifier?指定注入?Bean?的名稱,這樣歧義就消除了瞧柔。

7-1-2.BeanDefinitionReaderUtils#registerBeanDefinition

publicstaticvoidregisterBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException{// Register bean definition under primary name.// 獲取 beanNameString 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(Stringalias: aliases) {// 以 key-value 的形式注冊 bean 的別名,key 為別名廉邑,value 為 beanName哥蔚。見下文詳解registry.registerAlias(beanName,alias);}}}// DefaultListableBeanFactory.javapublicvoidregisterBeanDefinition(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) {thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition =this.beanDefinitionMap.get(beanName);// 這里判斷 BeanDefinition 是否存在if(existingDefinition !=null) {// 這里就是如果 Bean 定義以及存在杭隙,判斷是否可以覆蓋奶稠,默認(rèn)是可以的// Spring Boot 2.1開始這里會手動設(shè)置 allowBeanDefinitionOverriding 的值為 falseif(!isAllowBeanDefinitionOverriding()) {thrownewBeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}elseif(existingDefinition.getRole() < beanDefinition.getRole()){// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif(logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '"+ beanName +"' with a framework-generated bean definition: replacing ["+existingDefinition +"] with ["+ beanDefinition +"]");}}elseif(!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 是否已經(jīng)開始創(chuàng)建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 寫時復(fù)制一份,類似于 CopyOnWriteArrayListList updatedDefinitions =newArrayList<>(this.beanDefinitionNames.size() +1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;// 從單例 Bean 注冊名稱列表中刪除當(dāng)前 beanNameremoveManualSingletonName(beanName);}}// bean 不在創(chuàng)建狀態(tài)中else{// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);// 因為 ConcurrentHashMap 是無序的檐什,這里將 beanName 放入 ArrayList牵祟,記錄注冊順序this.beanDefinitionNames.add(beanName);// 從單例 Bean 注冊名稱列表中刪除當(dāng)前 beanNameremoveManualSingletonName(beanName);}this.frozenBeanDefinitionNames =null;}// 如果存在相同的 beanName 的 BeanDefinition深夯,或者 beanName 已經(jīng)存在單例對象,則將該 beanName 對應(yīng)的緩存信息诺苹、單例對象清除咕晋,因為這些對象都是由老的 BeanDefinition 創(chuàng)建的,需要被覆蓋掉收奔。再用新的 BeanDefinition 來創(chuàng)建這些緩存和單例對象if(existingDefinition !=null|| containsSingleton(beanName)) {resetBeanDefinition(beanName);}}// SimpleAliasRegistry.javapublicvoidregisterAlias(String name, Stringalias){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-registerreturn;}// 如果不允許覆蓋坪哄,拋出異常if(!allowAliasOverriding()) {thrownewIllegalStateException("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 是否存在循環(huán)引用质蕉。例如 A 的別名為 B,B的別名為AcheckForAliasCircle(name,alias);// 將 alias 和 name 以 key-value 對放入到 aliasMap 中翩肌,進(jìn)行緩存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 為 BeanDefinitionprivatefinalMap beanDefinitionMap =newConcurrentHashMap<>(256);// 保存 BeanDefinition 的注冊順序汰蓉,保存的是 beanName/** List of bean definition names, in registration order. */private volatileList beanDefinitionNames =newArrayList<>(256);

如果?BeanDefinition?是?AbstractBeanDefinition?類型,驗證?Bean?的格式是否正確棒卷。

這次效驗主要是對于?AbstractBeanDefinition?屬性中的?methodOverrides?的校驗顾孽,校驗?methodOverrides?是否與?工廠方法?并存或者?methodOverrides?中的方法根本不存在祝钢。

判斷該?beanName?的?BeanDefinition?是否已經(jīng)注冊過;如果存在判斷是否允許覆蓋若厚,允許的話直接替換拦英,不允許直接拋出異常。

默認(rèn)的情況下是允許的测秸,但是在 Spring Boot 2.1 開始這里會手動的設(shè)置為不允許疤估。

beanName?對應(yīng)的?BeanDefinition?以前沒有注冊過,判斷?bean?是否已經(jīng)開始創(chuàng)建霎冯;如果在創(chuàng)建中對?beanDefinitionMap?進(jìn)行加鎖 (這里雖然?beanDefinitionMap?是線程安全的?ConcurrentHashMap?铃拇,單個操作是線程安全的但多個操作不是,所以這里手動加鎖)沈撞,然后將?beanName?和?BeanDefinition?以?key-value?形式放入?beanDefinitionMap?緩存中慷荔,然后寫時復(fù)制一份?beanDefinitionNames?,將?beaName?緩存進(jìn)去缠俺,記錄?bean?的注冊順序显晶;如果不在創(chuàng)建直接將?BeanDefinition?和?beanName?分別放入?beanDefinitionMap?和?beanDefinitionNames?中。

最后判斷如果?BeanDefinition?已經(jīng)注冊過壹士,或者?beanName?已經(jīng)存在單例對象磷雇,則將該?beanName?對應(yīng)的緩存信息、單例對象清除躏救,因為這些對象都是由老的?BeanDefinition?創(chuàng)建的唯笙,需要被覆蓋掉。再用新的?BeanDefinition?來創(chuàng)建這些緩存和單例對象落剪。

總結(jié)

本文主要介紹了通過 XML 文件的方式注冊?Bean?睁本,我們可以重新梳理一下思路:

解析 XML 文件,構(gòu)建成?AbstractBeanDefinition (GenericBeanDefinition)?對象來存放所有解析出來的屬性忠怖。

將?AbstractBeanDefinition?呢堰、?beanName?、?aliasesArray?構(gòu)建成?BeanDefinitionHolder?對象凡泣。

最后通過?BeanDefinitionHolder?將?beanName?和?BeanDefinition?注冊到?DefaultListableBeanFactory?中枉疼,也就是保存起來。

上文提到的兩個比較重要的屬性?beanDefinitionNames?和?beanDefinitionMap?鞋拟,在后面都會多次用到骂维,可以重點關(guān)注一下。

最后贺纲,我模仿 Spring 寫了一個精簡版航闺,代碼會持續(xù)更新,現(xiàn)在是?0.0.1?版本。地址:?https://github.com/leisurexi/tiny-spring?潦刃。訪問新博客地址侮措,觀看效果更佳?https://leisurexi.github.io/

參考

《Spring 源碼深度解析》—— 郝佳

https://blog.csdn.net/v123411739/article/details/86669952

?收藏??糾錯

推薦文章

1.?SpringBoot入門系列(十一)統(tǒng)一異常處理的實現(xiàn)

2.?springboot web項目創(chuàng)建及自動配置分析(thymeleaf+flyway)

3.?在SpringBoot中使用SpringSecurity

4.?spring @Conditional 源碼解析 以及@ConditionalOnMissingBean 失效..

5.?Spring 聲明式事務(wù)處理的實現(xiàn)原理,來自面試官的窮追拷問

6.?使用鉤子促進(jìn)OAth 2.0與OpenID連接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乖杠,一起剝皮案震驚了整個濱河市分扎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胧洒,老刑警劉巖畏吓,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卫漫,居然都是意外死亡菲饼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門列赎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巴粪,“玉大人,你說我怎么就攤上這事粥谬。” “怎么了辫塌?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵漏策,是天一觀的道長。 經(jīng)常有香客問我臼氨,道長掺喻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任储矩,我火速辦了婚禮感耙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘持隧。我一直安慰自己即硼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布屡拨。 她就那樣靜靜地躺著只酥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呀狼。 梳的紋絲不亂的頭發(fā)上裂允,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音哥艇,去河邊找鬼绝编。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的十饥。 我是一名探鬼主播窟勃,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绷跑!你這毒婦竟也來了拳恋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤砸捏,失蹤者是張志新(化名)和其女友劉穎谬运,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垦藏,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梆暖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掂骏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轰驳。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弟灼,靈堂內(nèi)的尸體忽然破棺而出级解,到底是詐尸還是另有隱情,我是刑警寧澤田绑,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布勤哗,位于F島的核電站,受9級特大地震影響掩驱,放射性物質(zhì)發(fā)生泄漏芒划。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一欧穴、第九天 我趴在偏房一處隱蔽的房頂上張望民逼。 院中可真熱鬧,春花似錦涮帘、人聲如沸拼苍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽映屋。三九已至,卻和暖如春同蜻,著一層夾襖步出監(jiān)牢的瞬間棚点,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工湾蔓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瘫析,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像贬循,于是被迫代替她去往敵國和親咸包。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容