亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

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

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

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

概述


在上一篇解析<bean>標簽及其所有子標簽我們詳細探討了如何使用<bean>標簽來創建一個BeanDefintion對象。這一篇我們開始探討一下sPRing如何處理其它命名空間的xml標簽,比如spring擴展的http://www.springframework.org/schema/context、http://www.springframework.org/schema/p和http://www.springframework.org/schema/aop命名空間。

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

下面是DefaultBeanDefinitionDocumentReader對象使用BeanDefinitionParserDelegate對象來處理自定義命名空間的標簽的入口,

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); } }

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

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

parseCustomElement(Element ele)方法把處理節點的任務傳遞給parseCustomElement(Element ele, BeanDefinition containingBd)方法,這個方法的源碼如下。

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { // 獲取命名空間的uri String namespaceUri = getNamespaceURI(ele); // 根據NamespaceHandlerResolver對象獲取命名空間的處理器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; } // 調用NamespaceHandler的parse方法處理節點 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }

parseCustomElement(Element ele, BeanDefinition containingBd)首先通過節點的命名空間uri字符串獲取命名空間處理器NamespaceHandler對象,然后調用處理器的parse方法處理節點。spring定義了很多命名空間和它們對于的處理器類,因此,我們在這里需要清楚的是如何獲取NamespaceHandler對象。

獲取命名空間處理器NamespaceHandler對象

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

public interface NamespaceHandler { /** * DefaultBeanDefinitionDocumentReader實例化一個NamespaceHandler對象的時候會調用此方法 */ void init(); /** * 解析指定的的Element對象,并返回一個BeanDefinition對象。 */ BeanDefinition parse(Element element, ParserContext parserContext); /** * 解析指定的節點,并裝飾指定的BeanDefinitionHolder對象,最后返回一個已經裝飾的BeanDefinitionHolder對象。 * 這個方法在parse方法之后被調用 */ BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);}

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

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

NamespaceHandlerResolver 接口只定義了一個方法,并且Spring給出了一個默認的實現,那就是DefaultNamespaceHandlerResolver類,我們來看看這個類的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 是否實現了NamespaceHandler接口 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } // 實例化NamespaceHandler對象 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); // 初始化NamespaceHandler對象 namespaceHandler.init(); // 把NamespaceHandler對象緩存到handlerMappings對象中 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方法通過調用getHandlerMappings()方法獲得NamespaceHandler對象與命名空間URI的映射表,并從這個映射表中取到并返回對應的NamespaceHandler對象。resolve方法的重點就在于NamespaceHandler對象映射表的獲取,下面看看getHandlerMappings()方法的源代碼。

private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { // 這里的handlerMappingsLocation默認為"META-INF/spring.handlers" Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } // 創建一個線程安全的Map對象 Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); // 把Properties對象中的key-value復制到handlerMappings中國 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在第一次調用的時候會通過DefaultNamespaceHandlerResolver的handlerMappingsLocation屬性值(默認為META-INF/spring.handlers)獲取classes路徑和所有jar包中有的相應資源,我們來看看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) { // 獲取默認的ClassLoader對象 classLoaderToUse = ClassUtils.getDefaultClassLoader(); } // 使用ClassLoader對象來加載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啟動緩存 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包中獲取所有匹配的資源,并把這些資源文件的內容都保存到同一個Properties對象中作為loadAllProperties方法的返回值。

Spring自定義的NamespaceHandler對象

我們看看Spring都創建了哪些spring.handlers文件及其內容。 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文件中=號左邊的是命名空間uri,=號右邊的是命名空間處理器類的全名稱。

我們看看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對象 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()); // 設置為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() { // 注冊config標簽的的解析器 registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); // 注冊aspectj-autoproxy標簽的解析器 registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); // 注冊scoped-proxy標簽的裝飾器 registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // 注冊spring-configured標簽的解析器 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }}

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

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

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

spring為解析標簽自定義了一個BeanDefinitionParser接口,源碼如下。

public interface BeanDefinitionParser { /** * 解析指定的節點元素,并返回一個已經注冊到BeanDefinitionRegistry對象(BeanDefinition注冊表)中的BeanDefinition對象。 */ BeanDefinition parse(Element element, ParserContext parserContext);}

spring還為裝飾標簽和屬性定義了一個BeanDefinitionDecorator接口,源碼如下。

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

看到這里,我們再來說說NamespaceHandler和BeanDefinitionParser 以及BeanDefinitionDecorator二者的關系。 a. 對NamespaceHandler來說,它們可有可無。不過對于spring框架本身來說,這樣的設計提高了spring的可維護性、可擴展性和易讀性。 b. 對BeanDefinitionParser 和BeanDefinitionDecorator來說,它們必須受NamespaceHandler的管控和調度,否則它們將一無是處。

總結


(1)spring通過NamespaceHandler對象來解析自定義標簽和屬性,同時也用這個對象來裝飾BeanDefinitionHolder對象。

(2)DefaultNamespaceHandlerResolver用于管理NamespaceHandler實現類。因此可以從DefaultNamespaceHandlerResolver中根據命名空間uri獲取到對應的NamespaceHandler對象。

(3)NamespaceHandler實現類需要在/META-INF/spring.handler文件中注冊,否則不能被DefaultNamespaceHandlerResolver對象加載。

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


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩国产欧美精品一区二区三区| 亚洲国产成人精品女人久久久| 欧美肥老太性生活视频| 亚洲自拍小视频免费观看| 国产亚洲精品久久久久久777| 欧美另类交人妖| 日韩中文字幕网| 国产自摸综合网| 欧洲精品在线视频| 亚洲人成网站999久久久综合| 日韩激情av在线免费观看| 国产在线拍揄自揄视频不卡99| 色悠久久久久综合先锋影音下载| 成人激情综合网| 欧美成人精品一区二区| 国产成人啪精品视频免费网| 国产99久久精品一区二区 夜夜躁日日躁| 国产在线视频不卡| 91精品国产91久久久久| 亚洲乱码国产乱码精品精| 久久夜色精品国产| 国产一区二区三区网站| 精品日本美女福利在线观看| 97香蕉久久夜色精品国产| 欧美亚洲日本黄色| 中文字幕亚洲欧美日韩在线不卡| 81精品国产乱码久久久久久| 欧美激情久久久| 亚洲v日韩v综合v精品v| 国产精品偷伦免费视频观看的| 91av在线播放| 两个人的视频www国产精品| 欧美性xxxx| 日韩中文字幕在线精品| 日韩中文字幕免费| www国产亚洲精品久久网站| 国产丝袜一区二区三区免费视频| 欧美亚洲视频在线看网址| 91黑丝在线观看| 国产精品吹潮在线观看| 亚洲精品免费av| 狠狠色香婷婷久久亚洲精品| 色视频www在线播放国产成人| 国产午夜精品理论片a级探花| 亚洲欧美激情四射在线日| 国产欧美日韩中文字幕| 国产日韩欧美日韩| 欧美人在线观看| 欧美高清视频在线播放| 亚洲自拍偷拍色片视频| 亚洲一级黄色片| 91超碰中文字幕久久精品| 亚洲一区二区三区777| 韩国三级日本三级少妇99| 精品久久久久久久久久久久| 日韩av在线免费看| 一区三区二区视频| 久久亚洲综合国产精品99麻豆精品福利| 欧美性生交大片免网| 少妇av一区二区三区| 日韩视频免费看| 91av在线不卡| 欧美天天综合色影久久精品| 伊人一区二区三区久久精品| 亚洲一二在线观看| 亚洲美女动态图120秒| 国产亚洲激情视频在线| 亚洲精美色品网站| 91久久久久久国产精品| 久久精品国产亚洲精品2020| 欧美日韩国产一区二区三区| 在线日韩av观看| 亚洲欧美国产高清va在线播| 91精品国产自产91精品| 茄子视频成人在线| 国产视频久久久久久久| 久久久成人av| 日韩在线视频观看正片免费网站| 亚洲欧洲成视频免费观看| 成人午夜在线影院| 亚洲激情成人网| 国产精品国模在线| 国产成人精品av在线| 在线观看日韩视频| 欧美二区乱c黑人| 日韩av大片在线| 国产精品一区二区在线| 久久av资源网站| 国产精品久久久久久av| 热久久99这里有精品| 黄色成人在线免费| 日韩国产精品亚洲а∨天堂免| 成人免费淫片aa视频免费| 亚洲精品动漫100p| 九九热99久久久国产盗摄| 日韩精品视频在线观看免费| 欧美日韩在线视频首页| 日韩在线观看你懂的| 中文字幕日韩精品在线观看| 成人午夜在线视频一区| 欧美成人亚洲成人| 26uuu久久噜噜噜噜| 韩国一区二区电影| 韩剧1988在线观看免费完整版| 久久免费视频这里只有精品| 国产第一区电影| 亚洲精品日韩久久久| 欧美亚洲在线观看| 日本精品视频网站| 91久久久久久久一区二区| 国产69精品久久久久99| 高清欧美性猛交xxxx| 欧美高清视频在线观看| 91精品国产综合久久久久久久久| 久久97久久97精品免视看| 精品国产户外野外| 久久精品99久久香蕉国产色戒| 欧美一级高清免费播放| 亚洲一级一级97网| 97超碰国产精品女人人人爽| 日韩美女免费线视频| 亚洲国产精品悠悠久久琪琪| 久久久精品国产亚洲| 91av成人在线| 亚洲久久久久久久久久| 91色琪琪电影亚洲精品久久| 欧美整片在线观看| 欧美第一淫aaasss性| 欧美成人激情视频| 综合136福利视频在线| 成人www视频在线观看| 亚洲永久在线观看| 国产精品色婷婷视频| 欧美猛交免费看| 一区二区三区四区在线观看视频| 日韩在线播放av| 久久资源免费视频| 成人女保姆的销魂服务| 中文字幕日韩在线播放| 久久久久久18| 麻豆精品精华液| 久久久久久久久91| 久久中文字幕国产| 97激碰免费视频| 久久国产精品久久久| 91视频88av| 欧美高清不卡在线| 亚洲成年网站在线观看| 91综合免费在线| 日韩精品在线观看一区| 欧美激情一区二区三级高清视频| 九九热99久久久国产盗摄| 色婷婷成人综合| 国内精品久久久久伊人av| 日韩av网站导航| 日韩成人在线播放| 精品久久久久久久久久久久久| 8x拔播拔播x8国产精品| 91av视频在线免费观看| 成人免费视频在线观看超级碰| 欧美日韩美女在线| 狠狠躁夜夜躁人人躁婷婷91| 国产精品久久一区主播| 欧美性猛交xxxx久久久|