spring的applicationContext.xml文件與NamespaceHandler解析
applicationContext.xml文件解析
- Spring容器啟動(dòng),在創(chuàng)建BeanFactory時(shí),需要加載和解析當(dāng)前ApplicationContext對(duì)應(yīng)的配置文件applicationContext.xml,從而獲取bean相關(guān)的配置信息。
- 在內(nèi)部實(shí)現(xiàn)的調(diào)用關(guān)系為:ApplicationContext通過(guò)XmlBeanDefinitionReader來(lái)完成從applicationContext.xml獲取應(yīng)用配置的bean信息,并注冊(cè)到關(guān)聯(lián)的BeanFactory中。XmlBeanDefinitionReader的主要工作為解析xml文件的標(biāo)簽,包括從bean標(biāo)簽直接創(chuàng)建BeanDefinition,以及創(chuàng)建用于處理context:component-scan標(biāo)簽的BeanFactoryPostProcessor,然后間接創(chuàng)建BeanDefinitions。
- 整個(gè)解析過(guò)程如下:
- XmlBeanDefinitionReader創(chuàng)建DefaultBeanDefinitionDocumentReader對(duì)象實(shí)例,讀取XML文件并封裝成Document對(duì)象,然后將該Document對(duì)象作為參數(shù),調(diào)用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions,在registerBeanDefinitions中解析XML文件的內(nèi)容,從中獲取并生成beanDefinitions;
- registerBeanDefinitions方法:從XML文件對(duì)應(yīng)的Document對(duì)象獲取root Element,從root Element一直往下解析所有的xml標(biāo)簽Element;
- 針對(duì)每個(gè)xml標(biāo)簽Element,解析過(guò)程為:新增一個(gè)與該Element對(duì)應(yīng)的BeanDefinitionParserDelegate,根據(jù)該Element的命名空間nameSpaceUri,從NamespaceHandlerResolver獲取該nameSpaceUri對(duì)應(yīng)的NamespaceHandler;
- 其中NamespaceHandler內(nèi)部維護(hù)了xml標(biāo)簽和xml標(biāo)簽解析器BeanDefinitionParser的映射,所以由NamespaceHandler獲取當(dāng)前正在處理的xml標(biāo)簽Element對(duì)應(yīng)的標(biāo)簽處理器BeanDefinitionParser(如< bean …>標(biāo)簽處理器則直接創(chuàng)建BeanDefinition對(duì)象,而< component-scan …>標(biāo)簽則是創(chuàng)建一個(gè)BeanFactoryPostProcessor,之后由該BeanFactory后置處理器通過(guò)掃描指定包獲取類并生成BeanDefinition注冊(cè)到BeanFactory),由該BeanDefinitionParser從xml標(biāo)簽Element生成注冊(cè)到BeanFactory的BeanDefinition,或者生成BeanFactoryPostProcessor。
- 所以這里就需要定義每個(gè)標(biāo)簽對(duì)應(yīng)一個(gè)特定的標(biāo)簽解析器了,在spring內(nèi)部是通過(guò)BeanDefinitionParser接口來(lái)定義的。
NamespaceHandler
- 在Spring的設(shè)計(jì)當(dāng)中,通常每個(gè)標(biāo)簽都屬于一個(gè)特定的名稱空間來(lái)避免名稱沖突,如context名稱空間,mvc名稱空間等,每個(gè)名稱空間內(nèi)部可定義多個(gè)標(biāo)簽。
- 使用NamespaceHandler接口來(lái)維護(hù)每個(gè)名稱空間內(nèi)部的標(biāo)簽 和標(biāo)簽處理器之間的映射關(guān)系。這樣X(jué)mlBeanDefinitionReader在解析applicationContext.xml文件時(shí),遇到某個(gè)名稱空間,則獲取這名稱空間對(duì)應(yīng)的NamespaceHandler,然后通過(guò)NamespaceHandler進(jìn)一步獲取內(nèi)部標(biāo)簽對(duì)應(yīng)的標(biāo)簽處理器。
Spring內(nèi)部的使用和源碼實(shí)現(xiàn)
- 在spring-context,spring-webmvc,spring-beans等spring框架子項(xiàng)目的META-INF/spring.handlers文件中,定義標(biāo)簽命名空間與NamespaceHandler實(shí)現(xiàn)類之間的映射關(guān)系。
- 如下為spring-webmvc子項(xiàng)目的META-INF/spring.handlers文件內(nèi)容:
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
對(duì)應(yīng)的NamespaceHandler的實(shí)現(xiàn)類為:
package org.springframework.web.servlet.config; /** * {@link NamespaceHandler} for Spring MVC configuration namespace. * * @author Keith Donald * @author Jeremy Grelle * @author Sebastien Deleuze * @since 3.0 */ public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); } }
- XmlBeanDefinitionReader主要是從applicationContext.xml創(chuàng)建一個(gè)Document對(duì)象,交給子組件BeanDefinitionDocumentReader處理這個(gè)Document對(duì)象。而NamespaceHandler的實(shí)現(xiàn)類主要是在BeanDefinitionDocumentReader中在解析Document對(duì)象的Element元素時(shí)調(diào)用,如下:
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // 獲取一個(gè)NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 通過(guò)這個(gè)NamespaceHandler // 在它里面維護(hù)的parser集合中找到 // 與該標(biāo)簽對(duì)應(yīng)的parser,由該parser來(lái)執(zhí)行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
- 以上已經(jīng)說(shuō)明了通過(guò)XmlBeanDefinitionReader的NamespaceHandlerResolver維護(hù):名稱空間和NamespaceHandler的映射;通過(guò)NamespaceHandler來(lái)維護(hù)標(biāo)簽和Parser的映射,那么兩者是什么時(shí)候初始化注冊(cè)的呢?
- 其實(shí)NamespaceHandler和Parser的初始化,使用的是懶加載機(jī)制,即當(dāng)調(diào)用了NamespaceHandlerResolver的resolve方法時(shí),才進(jìn)行加載,加載之后進(jìn)行緩存。如下:
NamespaceHandler的初始化:在DefaultNamespaceHandlerResolver的getHandlerMappings方法實(shí)現(xiàn)。
在resolve方法中調(diào)用getHandlerMappings方法。
/** * Load the specified NamespaceHandler mappings lazily. */ private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return handlerMappings; }
- 從META-INF/spring.handlers文件加載鍵值對(duì),并緩存在類型為ConcurrentHashMap的handlerMappings中;
- 注意這里并沒(méi)有初始化NamespaceHandler,即handlerMappings的value還是String類型。
NamespaceHandler包含的parsers的初始化:在resolve方法中進(jìn)行懶加載初始化。
/** * Locate the {@link NamespaceHandler} for the supplied namespace URI * from the configured mappings. * @param namespaceUri the relevant namespace URI * @return the located {@link NamespaceHandler}, or {@code null} if none found */ @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { // 懶加載NamespaceHandler的handlerMappings Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } // 不是第一次調(diào)用,則已經(jīng)是NamespaceHandler類型了,可以直接返回 else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } // 第一次調(diào)用,由上面分析可知: // 剛開(kāi)始從META-INF/spring.handlers // 讀出時(shí),handlerMappings的value是字符串 else { String className = (String) handlerOrClassName; try { // 加載NamespaceHandler Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); // 調(diào)用init方法完成parsers的初始化 namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } } } protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }
NamespaceHandler的init方法實(shí)現(xiàn):各個(gè)NamespaceHandler接口實(shí)現(xiàn)類,在init方法中注冊(cè)xml的標(biāo)簽和Parser之間的映射關(guān)系:如下為context標(biāo)簽的名稱空間處理器ContextNamespaceHandler:
/** * {@link org.springframework.beans.factory.xml.NamespaceHandler} * for the '{@code context}' namespace. * * @author Mark Fisher * @author Juergen Hoeller * @since 2.5 */ public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); // <context:component-scan /> registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
插件機(jī)制拓展支持
如果需要開(kāi)發(fā)一個(gè)插件并自定義標(biāo)簽,然后融入到Spring容器中,則可以在自身插件項(xiàng)目中,基于NamespaceHandler來(lái)實(shí)現(xiàn),如在Dubbo項(xiàng)目中,就是利用了Spring的這個(gè)機(jī)制來(lái)容器到Spring框架的
基本步驟包括:
- 自定義xsd標(biāo)簽定義,并添加到插件項(xiàng)目的META-INF目錄中,路徑類似于一個(gè)普通pacakage下面的一個(gè)類;
- 實(shí)現(xiàn)BeanDefinitionParser接口,定義標(biāo)簽的處理邏輯;
- 實(shí)現(xiàn)NamespaceHandler接口,一般繼承Spring的NamespaceHandlerSupport即可。在init方法中,使用registerBeanDefinitionParser方法配置標(biāo)簽名稱和BeanDefinitionParser實(shí)現(xiàn)類的映射關(guān)系。
- 在META-INF/spring.schemas文件中,定義xsd文件全限定名與一個(gè)在applicationContext.xml文件中可以配置的命名空間url的映射;
- 在META-INF/spring.handlers文件中,定義以上命名空間url和該NamespaceHandler實(shí)現(xiàn)類的映射。
到此這篇關(guān)于spring的applicationContext.xml文件與NamespaceHandler解析的文章就介紹到這了,更多相關(guān)applicationContext與NamespaceHandler解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何使用SpringMVC的消息轉(zhuǎn)換器設(shè)置日期格式
這篇文章主要介紹了如何使用SpringMVC的消息轉(zhuǎn)換器設(shè)置日期格式問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07SpringBoot實(shí)現(xiàn)網(wǎng)站的登陸注冊(cè)邏輯記錄
登陸注冊(cè)功能是我們?nèi)粘i_(kāi)發(fā)中經(jīng)常遇到的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)網(wǎng)站的登陸注冊(cè)邏輯的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10Java Builder模式實(shí)現(xiàn)原理及優(yōu)缺點(diǎn)解析
這篇文章主要介紹了Java Builder模式實(shí)現(xiàn)原理及優(yōu)缺點(diǎn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java虛擬機(jī)內(nèi)存分配與回收策略問(wèn)題精細(xì)解讀
Java技術(shù)體系中所提倡的自動(dòng)內(nèi)存管理最終可以歸結(jié)為自動(dòng)化地解決了兩個(gè)問(wèn)題:給對(duì)象分配內(nèi)存以及回收分配給對(duì)象的內(nèi)存,本文讓我們來(lái)詳細(xì)了解2021-11-11Java實(shí)現(xiàn)日志文件監(jiān)聽(tīng)并讀取相關(guān)數(shù)據(jù)的方法實(shí)踐
本文主要介紹了Java實(shí)現(xiàn)日志文件監(jiān)聽(tīng)并讀取相關(guān)數(shù)據(jù)的方法實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05StringUtils,CollectionUtils判斷為空的方法和原生代碼哪個(gè)效率最高
這篇文章主要介紹了StringUtils,CollectionUtils判斷為空的方法和原生代碼哪個(gè)效率最高,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02