日本免费精品_最新日韩一区_亚洲视频一区在线_a在线视频观看_天天射夜夜骑_粉嫩av一区二区三区_欧美中日韩免费视频_综合图区欧美_国内精品美女在线观看_午夜精品久久久久久久男人的天堂

首頁 > 學院 > 開發設計 > 正文

Spring4.3.x 淺析xml配置的解析過程(1)——使用XmlBeanDefinitionReader解析xml配置

2019-11-10 20:14:33
字體:
來源:轉載
供稿:網友

概述


SPRing默認的BeanFactory是DefaultListableBeanFactory類,spring創建DefaultListableBeanFactory對象后,會把配置信息轉換成一個一個的BeanDefinition對象,并把這些BeanDefinition對象注冊到DefaultListableBeanFactory對象中,以供bean工廠創建bean實例。BeanDefinition對象存儲的是單個bean的配置信息,比如依賴類、scope、是否延遲加載等等。

Spring可以通過4種方式配置bean,其一是基于xml的配置,其二種是基于xml+注解的配置,其三是基于java+注解的配置,其四是基于property文件的配置。前兩種的配置信息使用XmlBeanDefinitionReader對象來解析;第三種的配置信息使用AnnotatedBeanDefinitionReader對象來解析;最后一種的配置信息使用PropertiesBeanDefinitionReader對象來解析。

而這里要討論的主題是xml配置的解析過程,因此我們的戰場在XmlBeanDefinitionReader,首先看看它的繼承結構,如下圖。

這里寫圖片描述

簡單的說XmlBeanDefinitionReader實現了BeanDefinitionReader接口,BeanDefinitionReader的設計用意是加載BeanDefintion對象,下面是它加載BeanDefintion的4個接口方法。

/** * 從單個指定的資源對象中加載BeanDefintion,并返回加載的BeanDefintion個數 */ int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; /** * 從多個指定的資源對象中加載BeanDefintion,并返回加載的BeanDefintion個數 */ int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; /** * 從單個指定的資源地址中加載BeanDefintion,并返回加載的BeanDefintion個數。 * 如果資源加載器是ResourcePatternResolver對象,那么location參數可以使用通配符。 */ int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; /** * 從多個指定的資源地址中加載BeanDefintion,并返回加載的BeanDefintion個數。 */ int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

這4個接口在后面會一一談到,這里還有個問題留給大家,既然是加載BeanDefintion,為什么不是返回所有加載的BeanDefintion對象,而是返回加載的個數?

在spring中直接使用XmlBeanDefinitionReader的容器有XmlWebapplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext。下面就從XmlWebApplicationContext為例來探索xml配置的解析過程。

XmlWebApplicationContext解析配置文件分為以下2個過程:

1) 創建并初始化XmlBeanDefinitionReader 對象。

2)使用XmlBeanDefinitionReader 對象提供的接口方法來加載BeanDefinition對象。

下面我們通過spring源碼來探討這個2個過程。

1 創建并初始化XmlBeanDefinitionReader 對象


XmlWebApplicationContext在創建完BeanFacotry的時候會調用它的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法來加載BeanDefinition,loadBeanDefinitions方法的代碼如下。

@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 根據給定的bean工廠創建新的XmlBeanDefinitionReader對象 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 使XmlBeanDefinitionReader對象與上下文對象在同一個資源環境中 beanDefinitionReader.setEnvironment(this.getEnvironment()); // 使用上下文對象為XmlBeanDefinitionReader對象的資源加載器 beanDefinitionReader.setResourceLoader(this); // 設置EntityResolver對象,用于加載XML的xsd或者dtd文件 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 鉤子方法,允許子類在加載bean definition之前進一步設置XmlBeanDefinitionReader // ->比如,更改XmlBeanDefinitionReader自己提供的DocumentLoader // 或者BeanDefinitionDocumentReader等默認對象 initBeanDefinitionReader(beanDefinitionReader); // 使用BeanDefinitionReader加載所有的BeanDefinition對象,見下面的代碼 loadBeanDefinitions(beanDefinitionReader); }

這段代碼是為使用BeanDefinitionReader對象加載BeanDefinitioin對象做準備工作。BeanDefinitionRegistry對象是BeanDefinition對象的注冊表,并且它是XmlBeanDefinitionReader對象創建時必須提供的,除此之外,其它的都使用默認或者由使用者提供。在XmlWebApplicationContext容器里,容器向BeanDefinitionReader提供了3個對象,第一個是資源環境Environment對象,它可用于判斷beans標簽的profile屬性,后面會談到;第二個是資源加載器ResourceLoader對象;最后一個是用于加載XML驗證文件(dtd或者xsd文件)的EntityResolver對象。如果這些還不夠,則可以擴展XmlWebApplicationContext容器,并重寫initBeanDefinitionReader方法對BeanDefinitionReader做更多的初始化。

2 使用XmlBeanDefinitionReader 對象來加載BeanDefinition


首先,還是先預覽一下XmlBeanDefinitionReader 加載BeanDefinition的流程: 這里寫圖片描述

上圖只是大致描述了XmlBeanDefinitionReader對象加載BeanDefinition的過程,還有些重點的旁枝末節沒有展現出來,但這并不影響我們了解XmlBeanDefinitionReader解析xml配置的過程。下面我們看看XmlWebApplicationContext如何使用XmlBeanDefinitionReader對象,以及XmlBeanDefinitionReader對象如何按照這個流程執行的。

創建完成BeanDefinitionReader對象后,XmlWebApplicationContext調用它的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法來使用BeanDefinitionReader對象。下面是loadBeanDefinitions(XmlBeanDefinitionReader reader)方法的代碼。

/** * 使用XmlBeanDefinitionReader加載所有的BeanDefinition對象 **/ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { // 獲取容器需要加載的配置文件的地址 String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { // 使用XmlBeanDefinitionReader一個一個的讀取spring配置文件 reader.loadBeanDefinitions(configLocation); } } }

這部分代碼是XmlBeanDefinitionReader 使用xml配置文件地址加載BeanDefinition對象的入口。這里所調用XmlBeanDefinitionReader的loadBeanDefinitions方法繼承自XmlBeanDefinitionReader的父類AbstractBeanDefinitionReader。下面代碼是loadBeanDefinitions方法在AbstractBeanDefinitionReader類中的實現。

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // 使用資源模式解析器解析配置文件的路徑并加載資源 try { // 加載所有與指定location參數匹配的所有資源 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 加載指定的資源中的所有BeanDefinition int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // 直接加載資源且只加載一個資源,默認使用DefaultResourceLoader的getResource方法 Resource resource = resourceLoader.getResource(location); // 加載指定的resource中的所有BeanDefinition int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }

這段代碼主要做的事情是,使用BeanDefinitionReader對象所持有的ResourceLoader來生成Resource對象。然后調用BeanDefinitionReader的loadBeanDefinitions(Resource… resources)或者loadBeanDefinitions(Resource resource)方法來執行加載BeanDefinition的代碼,其中,前一個方法通過多個資源文件來加載,后一個方法通過一個資源文件來加載。

首先我們從處理多個Resource對象的loadBeanDefinitions(Resource… resources)方法開始,這個方法已經在AbstractBeanDefinitionReader中有實現,并且XmlBeanDefinitionReader直接繼承了它,代碼如下。

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { // 一個一個資源文件的加載BeanDefinition // 此接口方法在AbstractBeanDefinitionReader沒有實現 // XmlWebApplicationContext使用XmlBeanDefinitionReader // ->則調用XmlBeanDefinitionReader的實現 counter += loadBeanDefinitions(resource); } return counter; }

這部分代碼的所做的事情是遍歷傳入的資源Resource對象,并調用loadBeanDefinitions(Resource resource)方法加載每一個資源。這個方法在AbstractBeanDefinitionReader并沒有實現,下面是此方法在XmlBeanDefinitionReader類中的實現代碼。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 讀取資源文件輸入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 根據指定的XML文件加載BeanDefinition 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(); } } }

loadBeanDefinitions(EncodedResource encodedResource)方法主要做的事情是從Resource對象中獲取xml文件輸入流,并用它來創建InputSource對象。然后調用XmlBeanDefinitionReader的doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法,代碼如下。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 加載Document對象 Document doc = doLoadDocument(inputSource, resource); // 注冊BeanDefinition return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescr這部分代碼所做的事情首先是獲取xml文檔的驗證模式,spring使用的xsd模式;然后調用DocumentLoader的loadDocument來讀取InputSource對象中的XML內容并創建Document對象,XmlBeanDefinitionReader默認的DocumentLoader為DefaultDocumentLoader;最后調用registerBeanDefinitions(Document doc, Resource resource)方法來處理剛創建的Document對象,下面是XmlBeanDefinitionReader類中registerBeanDefinitions方法的代碼。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 創建BeanDefinitionDocumentReader對象 // 默認為DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // documentReader需要持有當前的環境對象 documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); //首先創建XmlReaderContext對象 // 通過BeanDefinitionDocumentReader注冊BeanDefinition documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }

從這部分代碼可以看出,XmlBeanDefinitionReader使用BeanDefinitionDocumentReader 對象來加載Document對象中的配置信息。

這部分代碼主要做的事情有3步。第一步是創建BeanDefinitionDocumentReader對象,默認是DefaultBeanDefinitionDocumentReader;第二步是創建調用它的registerBeanDefinitions方法所需要的XmlReaderContext上下文對象,XmlReaderContext對象持有當前要讀取的資源、xml命名空間處理;第三步是調用documentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)處理Document對象。下面分別探討這三步的代碼。

第一步創建BeanDefinitionDocumentReader對象。

/** * 創建BeanDefinitionDocumentReader對象 **/ protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); }

第二步:創建XmlReaderContext上下文對象。

/** * 創建XmlReaderContext對象 **/ public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); } /** * 創建NamespaceHandlerResolver對象 **/ public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; } /** * 創建默認的命名空間處理器容器 **/ protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader()); }

第三步執行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); // 獲得root節點 Element root = doc.getDocumentElement(); // 注冊root節點中的所有BeanDefinition doRegisterBeanDefinitions(root); }

這一步是處理Document對象的重點也是入口。XML文件中只允許有一個根節點,上面的代碼所做的事情就是保存XmlReaderContext 對象并提取根節點,然后調用DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(Element root)方法,這個方法的代碼如下。

protected void doRegisterBeanDefinitions(Element root) { // 在這個方法中,遞歸所有嵌套<beans>元素。 // 為了正確地延用并保存<beans>元素的default-*屬性 // ->需要把父節點<beans>的delegate記錄下來,也許這個delegate可能為null。 BeanDefinitionParserDelegate parent = this.delegate; // 為當前的<beans>節點創建delgate對象 this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 檢查beans標簽上的profile屬性 // 聲明:public static final String PROFILE_ATTRIBUTE = "profile"; String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); // 解析根節點 parseBeanDefinitions(root, this.delegate); postProcessXml(root); // 解析完嵌套的<beans>標簽后,還原父節點的delegate this.delegate = parent; }

這段代碼主要也分成3步,第一步創建BeanDefinitionParserDelegate對象,這個對象的作用是代理DefaultBeanDefinitionDocumentReader解析BeanDefinition對象;第二步檢查bean標簽的profile屬性值是否與環境的匹配,如果不匹配則不處理而直接返回;第三步執行parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法。下面分別探討第一步和第三步。

(1) 創建BeanDefinitionParserDelegate對象,代碼如下

protected BeanDefinitionParserDelegate createDelegate( XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { // 創建BeanDefinitionParserDelegate BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); // 初始化delegate的Defaults // 如果當前<beans>節點的屬性值等于默認值,則使用父節點<beans>對應的屬性值。 delegate.initDefaults(root, parentDelegate); return delegate; }

(2) 執行DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法,代碼如下

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 檢查root節點的命名空間是否為默認命名空間 // spring配置文件中默認的命名空間為"http://www.springframework.org/schema/beans" if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); // 遍歷root節點下的所有子節點 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); } }

這段代碼主要是區分節點的命名空間,根據不同命名空間,調用相應的方法。如果節點在默認命名空間(http://www.springframework.org/schema/beans),則調用DefaultBeanDefinitionDocumentReader的parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)方法,否則調用BeanDefinitionParserDelegate 的parseCustomElement(Element ele)方法。

(1)處理自定義命名空間下的節點。執行BeanDefinitionParserDelegate的 parseCustomElement(Element ele) 方法,代碼如下

public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }

這部分代碼主要是獲取自定義命名空間的處理器,然后執行處理器的parse方法來創建一個BeanDefinition對象。

(2)處理默認命名空間下的節點。 執行DefaultBeanDefinitionDocumentReader的parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)方法,代碼如下。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 處理import節點元素 // 這里同樣是獲取配置文件并把注冊BeanDefinition對象 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 處理alias節點元素 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 處理bean節點元素 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 處理beans節點元素,遞歸調用doRegisterBeanDefinitions,詳見4.3 doRegisterBeanDefinitions(ele); } }

這段代碼是處理import、beans、alias、bean標簽的入口方法。

import標簽是引入其它spring配置文件;beans標簽是對bean進行分類配置,比如用一個beans來管理測試環境的bean,用另一個beans來管理生產環境的bean;alias標簽是為一個已定義了的bean取別名,它的name屬性值是bean的id,alias屬性值是要取的別名,多個別名用英文逗號、分號或者空格隔開;bean標簽的信息就是spring要實例化的對象。

不管是什么標簽,只有利用bean標簽才會生成BeanDefinition對象,下面重點探討的bean標簽處理。 執行DefaultBeanDefinitionDocumentReader的processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法,代碼如下。

/** * 解析bean節點,并注冊BeanDefinition對象 */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 創建BeanDefinitionHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 裝飾BeanDefinition bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 注冊已經創建好的BeanDefintion BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // 發送BeanDefinition注冊事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

這段代碼分成三步。第一步,根據傳入的Element對象(bean標簽的)調用代理對象的parseBeanDefinitionElement(Element ele)方法創建BeanDefinitionHolder 對象,這個對象持有創建好的BeanDefinition對象、bean的id和bean的別名。

第二步,調用代理對象的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)來對BeanDefinition對象再加工,主要是解析bean標簽中自定義屬性和自定義標簽。

第三步,調用工具類BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,這個方法用于注冊創建好的BeanDefinition。

前面兩步主要是完成BeanDefintion的創建和自定義,在這里不做更深入的探討,下面我們看看第三步,注冊BeanDefinition對象。 執行BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,把BeanDefinition對象注冊到BeanDefinitionRegistry 對象中

public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); // 把BeanDefinition對象注冊到BeanDefinitionRegistry 對象中 // 我們通過DefaultListableBeanFactory為例,介紹BeanDefinition的注冊 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 把bean id與別名關聯起來 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }

這部分代碼的作用是調用BeanDefinitionRegistry 的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法來注冊BeanDefinition,然后調用它的registerAlias(String name, String alias)來使bean的id和別名關聯起來。

DefaultListableBeanFactory實現了BeanDefinitionRegistry 接口的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法,代碼如下。

//--------------------------------------------------------------------- // Implementation of BeanDefinitionRegistry interface //--------------------------------------------------------------------- @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } // 覆蓋原來的BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // BeanDefintion已經注冊完成,而其他bean正在創建 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // 注冊BeanDefintion還未完成 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { // 重置BeanDefintiion resetBeanDefinition(beanName); } }

這段代碼的主要作用是DefaultListableBeanFactory把創建完成的BeanDefinition保存到Map對象beanDefinitionMap中,同時還要把以前已經創建好的bean(如果存在)銷毀掉。

總結


1. XmlBeanDefinitionReader的真正身份

XmlBeanDefinitionReader并不是xml配置的真正解析者,它只是相當于一個指揮官。當它收到一條需要加載BeanDefinition對象的任務后,它只會協調手下去完成相應的工作,它的手下有:

ResourceLoader,它把指定的配置文件地址封裝成Resource對象。

DocumentLoader,它把Resource對象中的XML文件內容轉換為Document對象。默認使用DocumentLoader的實現類DefaultDocumentLoader來加載Document對象。

BeanDefinitionDocumentReader,它把Document對象中包含的配置信息轉換成BeanDefinition對象并把它注冊到BeanDefintionRegistry對象中。默認使用DefaultBeanDefinitionDocumentReader來操作Document對象。在DefaultBeanDefinitionDocumentReader的實現中,它的責任是遍歷xml根節點下的子節點,并把處理bean標簽和自定義命名空間的標簽(比如aop:,context:,p:等)的細節委托給BeanDefinitionParserDelegate對象,BeanDefinitionParserDelegate才是真正解析配置文件的地方。

NamespaceHandlerResolver,用于獲取非默認命名空間的處理器,默認是DefaultNamespaceHandlerResolver對象。它雖然由XmlBeanDefinitionReader提供,但真正的使用者是BeanDefinitionParserDelegate類。

2. 更改XmlBeanDefinitionReader的默認對象

比如需要更改DocumentLoader、NamespaceHandlerResolver或者BeanDefinitionDocumentReader,甚至是ResourceLoader。

繼承XmlWebApplicationContext,并重寫initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader),這個方法在XmlWebApplicationContext只是一個空實現,特意留給子類在使用XmlBeanDefinitionReader對象加載BeanDefinition之前對這個對象進行定制的。

遺留問題

這一篇文章只是大致的過了一下XmlBeanDefinitionReader如何根據指定的配置文件地址來加載并注冊BeanDefintion對象,至于以下細節并沒有過多的描述。

ResourceLoader如何根據指定的location生成Resource對象。

DocumentLoader如何根據xml輸入流生成為Document對象。

Spring如何解析bean標簽的屬性以及子節點。

Spring如何解析非默認命名空間的配置。

Spring如何解析注解配置。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
久久久99精品久久| 免费视频最近日韩| 国产视频二区| 中文字幕五月欧美| 亚洲一区在线观看免费| 色综合婷婷久久| 日韩精品视频免费在线观看| 精品中文字幕在线| 亚洲 欧美综合在线网络| 国产 欧美 日韩 在线| 樱花草www在线观看| 91xxx在线观看| 国产亚洲短视频| 91精品国产91综合久久蜜臀| 国产手机视频一区二区| 日韩国产91| 精品视频—区二区三区免费| 欧美日韩三级在线| 国产日韩av高清| 国产欧美日韩精品综合| 欧美日韩精品欧美日韩精品| 欧美国产小视频| 亚洲国产福利| 国产在线不卡av| 精品三级av| 一区二区三区中文字幕在线观看| www.狠狠干| 日韩欧美在线影院| 亚洲一区中文字幕在线观看| 国产在线一区二区视频| 国产激情在线视频| 91久久精品午夜一区二区| 欧美精选午夜久久久乱码6080| 一区二区三区中文字幕在线观看| 久久精品夜夜夜夜久久| 欧美日韩亚洲天堂| 91精品在线国产| 一级国产黄色片| 久久久久蜜桃| 精品成人久久久| 国产不卡在线| 亚洲一区中文字幕在线观看| 精品一二三区视频| 欧美日韩免费精品| 亚洲三级av| 欧美日韩激情在线一区二区三区| 国产网站欧美日韩免费精品在线观看 | 天天综合天天做| 精品国产99久久久久久 | 亚洲黄页一区| 国产欧美 在线欧美| 中文字幕在线精品| 一区三区二区视频| 欧美国产亚洲一区| 一区二区不卡视频在线观看| 国产综合成人久久大片91| 伊人www22综合色| 久久视频免费看| 在线视频第一页| 欧美日韩高清在线一区| 国产无套粉嫩白浆在线2022年| 不卡中文一二三区| 国产欧美日韩精品高清二区综合区 | 精品视频1区2区3区| 日韩福利视频导航| 91精品久久久久| 精品成人久久久| 91精品国产91久久久久久青草| 国产一区免费视频| 欧美日韩视频免费播放| 国产资源在线观看入口av| 国产视频一区三区| 刘玥91精选国产在线观看| 国产在线www| 日韩一级免费在线观看| 亚洲一区中文字幕在线观看| 亚洲免费中文字幕| 久艹在线视频| 国产成人综合精品| 中文字幕在线亚洲| www.三级.com| 国产欧美日韩在线视频| 日韩精品丝袜在线| 午夜一区二区三区| 国产一二三精品| 国产视频97| 亚洲社区在线| 日韩欧美一级精品久久| 美女免费视频一区| 在线观看国产一区| 亚洲视频一二三四| 91精品视频播放| 久久久久久91| 亚洲免费婷婷| 国产日韩1区| 欧美 日韩 国产在线观看| av三级在线播放| 亚洲女人天堂色在线7777| 久久中文免费视频| 亚洲视频在线观看三级| 男人的天堂网av| 色综合婷婷久久| 中文在线中文字幕| 精品中文在线| 色综合天天综合网天天狠天天 | 亚洲国内精品视频| 国产三级在线播放| 日韩高清不卡一区| 精品国产1区2区3区| 精品视频1区2区3区| 亚洲三级av| 亚洲最新免费视频| 99久久精品国产成人一区二区| 成年人黄国产| 欧美三级在线播放| 国产网站欧美日韩免费精品在线观看| 国产激情在线| 精品久久久91| www.av中文字幕| 国产拍揄自揄精品视频麻豆| 国产综合成人久久大片91| 一区二区中文视频| 中文国产字幕在线观看| 亚洲最大黄色| 欧美日韩精品在线播放| 精品在线一区二区三区| 亚洲欧美日本另类| 日本亚洲视频在线| 一区在线播放视频| 亚洲福利精品视频| www.精品视频| 在线国产1区| 亚洲成av人片在线| 日韩精品资源二区在线| 日韩久久在线| 亚洲男女av一区二区| 欧美国产一区视频在线观看| av中文网站| 91久久久久久久久| 欧美日韩午夜在线视频| av中文资源在线| 九九在线精品| 日本а中文在线天堂| 欧美黄页在线免费观看| 久久精品99国产国产精| 日韩欧美亚洲区| 欧美日韩国产高清一区| 天天综合天天添夜夜添狠狠添| 日本精品在线播放| 欧美一级免费看| 日本亚洲欧美| 欧美三级中文字幕在线观看| 黄色一级大片在线免费看国产一| 日韩一级中文字幕| 精品亚洲永久免费| 国产伦精品免费视频| 亚洲二区自拍| 一区在线视频观看| 日韩不卡在线播放| 欧美三级中文字幕| 日韩精品在线免费看| 精品视频999| 男人的天堂网av| 精品一区二区在线观看视频| 欧美国产亚洲一区| 日韩精品在线网站| 精品成a人在线观看| 韩国一区二区av| 在线一区二区不卡| 日韩欧美一卡二卡| 精品久久久精品| 国产男女av| 精品极品三级久久久久| 日韩精品在线免费观看视频| 欧美日韩色综合| 日韩在线视频一区二区三区| 日韩视频国产视频| 欧美日韩免费视频| 亚洲欧洲综合另类| 日韩你懂的电影在线观看| 国产网站av| 日韩精品a在线观看91| 亚洲福利视频在线| 亚洲欧美小说国产图片| 欧美一级一级性生活免费录像| 亚洲第一中文字幕| 国产99亚洲| 欧美一级日韩一级| 亚洲一区不卡在线| 欧美日韩在线不卡| 成人一区而且| 亚洲黄色www| 日韩在线视频一区| 国产一级粉嫩xxxx| 日韩三级免费观看| 亚洲综合日韩中文字幕v在线| av免费在线播放| 欧美一级久久| 国内精品不卡| 亚洲欧洲日产国码av系列天堂| www.狠狠干| 黄色片网站在线| 欧美 日韩 中文字幕| 精品国产99久久久久久| 午夜精品一区二区三区免费视频| 欧美日韩精品综合在线| 91亚洲国产| 国产成免费视频| 91精品综合久久| 国内不卡的二区三区中文字幕| 亚洲国内精品视频| 中文不卡在线| 日韩高清不卡一区| 免费国产h视频在线观看86| 日韩精品视频免费| 亚洲国产91精品在线观看| 国产在线观看91| 国产黄在线观看| 欧美 日韩 国产 在线| 国产xxx在线| 亚洲综合中文字幕在线| 欧美性生交大片免费| 国内精品不卡| 国产丝袜在线播放| 日韩中文字幕| 影音先锋一区二区资源站| 欧美日韩精品中文字幕| 国内精品99| 国产婷婷在线观看| 精品网站999| 一区二区91| 免费视频国产一区| 国精品产品一区| 精品人妻一区二区免费视频| 国产日韩在线亚洲字幕中文| 国产91欧美| 久久综合九色欧美综合狠狠| 国产香蕉免费精品视频| 日韩美女中文字幕| 国产小视频在线观看| 欧美亚洲国产免费| 国产在线看一区| 亚洲免费观看视频| 欧美sm一区| 中文字幕第一页在线| 天堂√8在线中文| 久久蜜桃一区二区| 欧美区高清在线| 91精品在线看V| 欧美日韩亚洲91| 91久久久精品国产| 黄色在线播放网站| 国产欧美综合在线| 欧美日韩国产91| 黄色片网站在线| 91精品国产色综合久久不卡蜜臀 | 欧美日韩国产一区中文午夜| 日韩美女中文字幕| 二区中文字幕| 日韩中文字幕综合| 国产一二三精品| 国产中文在线观看| 久久久精品福利| 日韩激情一区| 日韩精品在线观| 亚洲欧美99| 美女黄a一级视频| 国产乱国产乱300精品| 一区二区在线视频播放| 国产永久免费高清在线观看| 精品视频—区二区三区免费| 国产xxx在线| 中文字幕日韩在线观看| 成年人看的羞羞网站| 日韩亚洲一区在线| 欧美日韩久久不卡| 中文字幕日韩国产| 国产亚洲短视频| 91精品国产91久久久久久不卡 | 91精品国产综合久久精品app| 国产99在线 | 亚洲| 日韩精品资源二区在线| 二区中文字幕| 日韩视频一区| 亚洲视频在线观看三级| 亚洲 欧美 中文字幕| 欧美日韩中国免费专区在线看| 日韩欧美99| 日韩在线中文视频| 精品人妻一区二区三区视频| 91精选在线| 亚州福利视频| 午夜一区二区三区免费| 成人久久久久| 麻豆精品视频入口| 欧美日韩乱国产| 国产欧美日韩综合精品| 婷婷久久综合九色国产成人| 99视频精品全国免费| 亚洲制服丝袜一区| 不卡一区2区| 97久久精品午夜一区二区| 中文字幕 欧美日韩| 亚洲 欧美综合在线网络| 国产婷婷色一区二区三区| 日韩欧美亚洲国产| 欧美三级日本三级少妇99| 国产欧美自拍一区| 一区二区三区视频网站| 91精品在线看V| 在线一区av| 99久久婷婷| 国产视频二区| 日韩欧美中文免费| 日韩视频国产视频| www.中文字幕在线| 中文字幕亚洲在| 久久精品国产91精品亚洲| 久久99久久久久| 国产欧美日韩亚洲| 最近中文字幕在线中文视频| 91精品国产99| 欧美亚洲天堂网| 91精品视频播放| 精品欧美久久久| 最近高清中文在线字幕在线观看1| 日韩在线视频观看| 一级免费a一片| 欧美日韩在线视频一区| 国产在线二区| 色妇色综合久久夜夜| 欧美日韩国产综合视频在线观看中文| 久久99精品久久久| 国产在线第一页| 最新中文在线视频| 精品国产欧美成人夜夜嗨| 伊人www22综合色| 99亚洲视频| 日韩精品视频中文字幕| 日韩欧美中文字幕精品| 亚洲综合在线电影| 日韩欧美中文字幕在线观看| 精品久久在线| 欧美日韩国产中字| 欧美日韩国产在线看| 亚洲欧洲精品在线| 中文字幕精品一区二区三区在线| 亚洲永久免费视频| 中文国产字幕在线观看| 日韩中文字幕综合| 日韩欧美中文字幕在线观看| www日韩欧美| 国产在线不卡av| 999在线视频| 日韩欧美国产1| 日本免费在线视频不卡一不卡二| 国产亚洲一级| 精品视频久久| 国产黄色片大全| 精品中文字幕视频| 久久久久蜜桃| 韩国一区二区av| 中文字幕欧美日韩在线| 日韩欧美资源站| 99精品一级欧美片免费播放| 中文字幕欧美人妻精品一区蜜臀| 亚洲免费观看在线观看| 欧美日韩中文字幕| 免费精品国产自产拍观看| 国产福利一区在线观看| 91精品久久久久久久久| 在线一区二区不卡| www日韩欧美| 欧美日韩国产一二三| 日韩精品视频在线播放| 国产免费不卡av| 欧美日韩第一区日日骚| 中文精品电影| 国产成人精品网址| 不卡中文一二三区| 欧美三级三级三级爽爽爽| 国产91一区| 精品一二线国产| 亚洲制服一区| 日韩在线视频免费观看| 狠狠色噜噜狠狠狠狠97| 欧美二区在线观看| 久久人人88| 日韩中文欧美在线| 欧美不卡一区二区| www.中文字幕在线| 91九色在线播放| 日韩一级二级三级精品视频| 欧美三级三级三级爽爽爽| 国产一级免费在线观看| 国产网站av| 久久av免费| 91精品视频国产| 日韩久久精品网| 欧美日韩免费精品|