国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Spring4.3.x 淺析xml配置的解析過程(5)——解析自定義命名空間的標(biāo)簽

2019-11-09 19:46:42
字體:
供稿:網(wǎng)友

概述


在上一篇解析<bean>標(biāo)簽及其所有子標(biāo)簽我們?cè)敿?xì)探討了如何使用<bean>標(biāo)簽來創(chuàng)建一個(gè)BeanDefintion對(duì)象。這一篇我們開始探討一下sPRing如何處理其它命名空間的xml標(biāo)簽,比如spring擴(kuò)展的http://www.springframework.org/schema/context、http://www.springframework.org/schema/p和http://www.springframework.org/schema/aop命名空間。

經(jīng)過前面的探討,我們知道XmlBeanDefinitionReader使用BeanDefinitionDocumentReader對(duì)象把Document對(duì)象中包含的配置信息轉(zhuǎn)換成BeanDefinition對(duì)象并把它注冊(cè)到BeanDefintionRegistry對(duì)象中。默認(rèn)使用DefaultBeanDefinitionDocumentReader來操作Document對(duì)象。在DefaultBeanDefinitionDocumentReader的實(shí)現(xiàn)中,它的責(zé)任是遍歷xml根節(jié)點(diǎn)下的子節(jié)點(diǎn),并把處理bean標(biāo)簽和自定義命名空間的標(biāo)簽(比如aop:,context:,p:等)的細(xì)節(jié)委托給BeanDefinitionParserDelegate對(duì)象,BeanDefinitionParserDelegate才是真正解析配置文件的地方。

下面是DefaultBeanDefinitionDocumentReader對(duì)象使用BeanDefinitionParserDelegate對(duì)象來處理自定義命名空間的標(biāo)簽的入口,

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 檢查root節(jié)點(diǎn)的命名空間是否為默認(rèn)命名空間 // spring配置文件中默認(rèn)的命名空間為"http://www.springframework.org/schema/beans" if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); // 遍歷root節(jié)點(diǎn)下的所有子節(jié)點(diǎn) for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 檢查子節(jié)點(diǎn)的命名空間是否為默認(rèn)命名空間 if (delegate.isDefaultNamespace(ele)) { // 解析默認(rèn)命名空間的元素節(jié)點(diǎn) parseDefaultElement(ele, delegate); } else { // 解析自定義元素節(jié)點(diǎn) delegate.parseCustomElement(ele); } } } } else { // 解析自定義元素節(jié)點(diǎn) delegate.parseCustomElement(root); } }

parseBeanDefinitions方法的主要事情是區(qū)分節(jié)點(diǎn)標(biāo)簽是否是默認(rèn)命名空間的標(biāo)簽,以及遍歷根節(jié)點(diǎn)下的子節(jié)點(diǎn)。在這里對(duì)于任何一個(gè)節(jié)點(diǎn),如果是默認(rèn)命名空間的則調(diào)用DefaultBeanDefinitionDocumentReader方法parseDefaultElement處理,否則調(diào)用BeanDefinitionParserDelegate 的parseCustomElement方法來處理。這篇文章的主題是探討spring如何解析自定義命名空間的標(biāo)簽,因此我們的入口是parseCustomElement方法,如下是這個(gè)方法的源代碼。

public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }

parseCustomElement(Element ele)方法把處理節(jié)點(diǎn)的任務(wù)傳遞給parseCustomElement(Element ele, BeanDefinition containingBd)方法,這個(gè)方法的源碼如下。

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { // 獲取命名空間的uri String namespaceUri = getNamespaceURI(ele); // 根據(jù)NamespaceHandlerResolver對(duì)象獲取命名空間的處理器NamespaceHandler對(duì)象。 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 調(diào)用NamespaceHandler的parse方法處理節(jié)點(diǎn) return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }

parseCustomElement(Element ele, BeanDefinition containingBd)首先通過節(jié)點(diǎn)的命名空間uri字符串獲取命名空間處理器NamespaceHandler對(duì)象,然后調(diào)用處理器的parse方法處理節(jié)點(diǎn)。spring定義了很多命名空間和它們對(duì)于的處理器類,因此,我們?cè)谶@里需要清楚的是如何獲取NamespaceHandler對(duì)象。

獲取命名空間處理器NamespaceHandler對(duì)象

下面是spring定義的NamespaceHandler接口的代碼。

public interface NamespaceHandler { /** * DefaultBeanDefinitionDocumentReader實(shí)例化一個(gè)NamespaceHandler對(duì)象的時(shí)候會(huì)調(diào)用此方法 */ void init(); /** * 解析指定的的Element對(duì)象,并返回一個(gè)BeanDefinition對(duì)象。 */ BeanDefinition parse(Element element, ParserContext parserContext); /** * 解析指定的節(jié)點(diǎn),并裝飾指定的BeanDefinitionHolder對(duì)象,最后返回一個(gè)已經(jīng)裝飾的BeanDefinitionHolder對(duì)象。 * 這個(gè)方法在parse方法之后被調(diào)用 */ BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);}

Spring定義了NamespaceHandlerResolver接口來獲取命名空間處理器,這個(gè)接口的定義如下。

public interface NamespaceHandlerResolver { /** * 解析命名空間的URI字符串并返回一個(gè)對(duì)應(yīng)的NamespaceHandler對(duì)象,如果沒有找到,則返回null */ NamespaceHandler resolve(String namespaceUri);}

NamespaceHandlerResolver 接口只定義了一個(gè)方法,并且Spring給出了一個(gè)默認(rèn)的實(shí)現(xiàn),那就是DefaultNamespaceHandlerResolver類,我們來看看這個(gè)類的resolve(String namespaceUri)方法,源代碼如下。

@Override public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); // 檢查handlerClass 是否實(shí)現(xiàn)了NamespaceHandler接口 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } // 實(shí)例化NamespaceHandler對(duì)象 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); // 初始化NamespaceHandler對(duì)象 namespaceHandler.init(); // 把NamespaceHandler對(duì)象緩存到handlerMappings對(duì)象中 handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } }

DefaultNamespaceHandlerResolver的resolve方法通過調(diào)用getHandlerMappings()方法獲得NamespaceHandler對(duì)象與命名空間URI的映射表,并從這個(gè)映射表中取到并返回對(duì)應(yīng)的NamespaceHandler對(duì)象。resolve方法的重點(diǎn)就在于NamespaceHandler對(duì)象映射表的獲取,下面看看getHandlerMappings()方法的源代碼。

private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { // 這里的handlerMappingsLocation默認(rèn)為"META-INF/spring.handlers" Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } // 創(chuàng)建一個(gè)線程安全的Map對(duì)象 Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); // 把Properties對(duì)象中的key-value復(fù)制到handlerMappings中國(guó) CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return this.handlerMappings; }

getHandlerMappings在第一次調(diào)用的時(shí)候會(huì)通過DefaultNamespaceHandlerResolver的handlerMappingsLocation屬性值(默認(rèn)為META-INF/spring.handlers)獲取classes路徑和所有jar包中有的相應(yīng)資源,我們來看看PropertiesLoaderUtils是怎么使用 loadAllProperties(String resourceName, ClassLoader classLoader)加載資源的,代碼如下。

public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException { Assert.notNull(resourceName, "Resource name must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { // 獲取默認(rèn)的ClassLoader對(duì)象 classLoaderToUse = ClassUtils.getDefaultClassLoader(); } // 使用ClassLoader對(duì)象來加載class路徑和所有jar包下所匹配的資源 Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) : ClassLoader.getSystemResources(resourceName)); Properties props = new Properties(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); URLConnection con = url.openConnection(); // 為JNLPCachedJarURLConnection啟動(dòng)緩存 ResourceUtils.useCachesIfNecessary(con); InputStream is = con.getInputStream(); try { if (resourceName.endsWith(".xml")) { // 加載xml文件 props.loadFromXML(is); } else { // 加載properties文件 props.load(is); } } finally { is.close(); } } return props; }

loadAllProperties方法從classes路徑下和所有jar包中獲取所有匹配的資源,并把這些資源文件的內(nèi)容都保存到同一個(gè)Properties對(duì)象中作為loadAllProperties方法的返回值。

Spring自定義的NamespaceHandler對(duì)象

我們看看Spring都創(chuàng)建了哪些spring.handlers文件及其內(nèi)容。 a. spring-beans包下的

http/://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandlerhttp/://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandlerhttp/://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

b. spring-context包下的

http/://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandlerhttp/://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandlerhttp/://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandlerhttp/://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandlerhttp/://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

c. spring-aop包下的

http/://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

d. spring-tx包下的

http/://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

e. spring-jdbc包下的

http/://www.springframework.org/schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler

e. spring-webmvc包下的

http/://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

spring.handlers文件中=號(hào)左邊的是命名空間uri,=號(hào)右邊的是命名空間處理器類的全名稱。

我們看看p命名空間的處理器SimplePropertyNamespaceHandler類的源碼,如下。

public class SimplePropertyNamespaceHandler implements NamespaceHandler { private static final String REF_SUFFIX = "-ref"; @Override public void init() { } @Override public BeanDefinition parse(Element element, ParserContext parserContext) { parserContext.getReaderContext().error( "Class [" + getClass().getName() + "] does not support custom elements.", element); return null; } @Override public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { // p命名空間只處理屬性 if (node instanceof Attr) { Attr attr = (Attr) node; String propertyName = parserContext.getDelegate().getLocalName(attr); String propertyValue = attr.getValue(); // 獲取BeanDefinition的MutablePropertyValues對(duì)象 MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues(); if (pvs.contains(propertyName)) { parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " + "both <property> and inline syntax. Only one approach may be used per property.", attr); } if (propertyName.endsWith(REF_SUFFIX)) { // 獲取屬性名稱 propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length()); // 設(shè)置為bean引用 pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue)); } else { pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue); } } return definition; }}

再看看aop命名空間的處理器AopNamespaceHandler 類的源碼,如下。

public class AopNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { // 注冊(cè)config標(biāo)簽的的解析器 registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); // 注冊(cè)aspectj-autoproxy標(biāo)簽的解析器 registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); // 注冊(cè)scoped-proxy標(biāo)簽的裝飾器 registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // 注冊(cè)spring-configured標(biāo)簽的解析器 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }}

上面兩個(gè)例子是spring創(chuàng)建NamespaceHandler實(shí)現(xiàn)類的兩種方式,第一種是直接實(shí)現(xiàn)NamespaceHandler接口提供的方法,這種方式適合于命名空間中標(biāo)簽只有一個(gè)或者解析標(biāo)簽和屬性的過程很簡(jiǎn)單,比如p命名空間;第二種是繼承抽象類NamespaceHandlerSupport 并實(shí)現(xiàn)init方法,在init方法中注冊(cè)標(biāo)簽的解析器和裝飾器以及屬性的裝飾器,spring中大多數(shù)命名空間處理器都使用這種方式。

NamespaceHandlerSupport向子類提供了三個(gè)方法分別用于注冊(cè)標(biāo)簽的解析、標(biāo)簽的裝飾器、屬性的裝飾器,如下。

/** * 注冊(cè)標(biāo)簽的解析器BeanDefinitionParser對(duì)象 */ protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); } /** * 注冊(cè)標(biāo)簽的裝飾器BeanDefinitionDecorator對(duì)象 */ protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) { this.decorators.put(elementName, dec); } /** * 注冊(cè)屬性的裝飾器BeanDefinitionDecorator對(duì)象 */ protected final void registerBeanDefinitionDecoratorForAttribute(String attrName, BeanDefinitionDecorator dec) { this.attributeDecorators.put(attrName, dec); }

spring為解析標(biāo)簽自定義了一個(gè)BeanDefinitionParser接口,源碼如下。

public interface BeanDefinitionParser { /** * 解析指定的節(jié)點(diǎn)元素,并返回一個(gè)已經(jīng)注冊(cè)到BeanDefinitionRegistry對(duì)象(BeanDefinition注冊(cè)表)中的BeanDefinition對(duì)象。 */ BeanDefinition parse(Element element, ParserContext parserContext);}

spring還為裝飾標(biāo)簽和屬性定義了一個(gè)BeanDefinitionDecorator接口,源碼如下。

public interface BeanDefinitionDecorator { /** * 根據(jù)指定Node對(duì)象裝飾BeanDefinitionHolder對(duì)象,這個(gè)Node對(duì)象可以是屬性,即Attr對(duì)象, * 也可以是標(biāo)簽元素,即Element對(duì)象 * 最后返回一個(gè)裝飾好了的BeanDefinitionHolder 對(duì)象 */ BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext);}

看到這里,我們?cè)賮碚f說NamespaceHandler和BeanDefinitionParser 以及BeanDefinitionDecorator二者的關(guān)系。 a. 對(duì)NamespaceHandler來說,它們可有可無(wú)。不過對(duì)于spring框架本身來說,這樣的設(shè)計(jì)提高了spring的可維護(hù)性、可擴(kuò)展性和易讀性。 b. 對(duì)BeanDefinitionParser 和BeanDefinitionDecorator來說,它們必須受NamespaceHandler的管控和調(diào)度,否則它們將一無(wú)是處。

總結(jié)


(1)spring通過NamespaceHandler對(duì)象來解析自定義標(biāo)簽和屬性,同時(shí)也用這個(gè)對(duì)象來裝飾BeanDefinitionHolder對(duì)象。

(2)DefaultNamespaceHandlerResolver用于管理NamespaceHandler實(shí)現(xiàn)類。因此可以從DefaultNamespaceHandlerResolver中根據(jù)命名空間uri獲取到對(duì)應(yīng)的NamespaceHandler對(duì)象。

(3)NamespaceHandler實(shí)現(xiàn)類需要在/META-INF/spring.handler文件中注冊(cè),否則不能被DefaultNamespaceHandlerResolver對(duì)象加載。

(4)命名空間中有多個(gè)標(biāo)簽需要解析和裝飾,命名空間處理器類最好繼承NamespaceHandlerSupport ,并為標(biāo)簽分別定義BeanDefinitionParser解析器或者BeanDefinitionDecorator裝飾器。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 张掖市| 大英县| 宁强县| 泽州县| 开封市| 湘潭县| 武夷山市| 吉木萨尔县| 商城县| 射阳县| 北辰区| 沭阳县| 曲水县| 肇州县| 康马县| 陆川县| 敦化市| 台江县| 青海省| 酒泉市| 轮台县| 柯坪县| 双峰县| 唐河县| 武山县| 闸北区| 梓潼县| 搜索| 二连浩特市| 岳阳县| 广安市| 鄂伦春自治旗| 芮城县| 祁阳县| 吴川市| 高青县| 来安县| 瑞安市| 弥渡县| 宝山区| 松阳县|