博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 源码解析(七)解析自定义命名空间的标签
阅读量:6636 次
发布时间:2019-06-25

本文共 11365 字,大约阅读时间需要 37 分钟。

上一章我们介绍了Spring如何创建bean,<bean></bean>的命名空间是Spring默认的命名空间,那么对于<tx:advice></tx:advice>、<mvc:annotation-driven></mvc:annotation-driven>这种自定义的标签该如何解析呢?下面就以这几个标签为例进行说明,同时也说明下声明式Spring事物,还有我们在进行SpringMVC开发时配置的<mvc:annotation-driven/>到底起什么作用也会简单介绍。

现在我们回到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中

//org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader  1 /** 2      * Parse the elements at the root level in the document: 3      * "import", "alias", "bean". 4      * @param root the DOM root element of the document 5      */ 6     protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 7         if (delegate.isDefaultNamespace(root)) { 8             NodeList nl = root.getChildNodes(); 9             for (int i = 0; i < nl.getLength(); i++) {10                 Node node = nl.item(i);11                 if (node instanceof Element) {12                     Element ele = (Element) node;13                     if (delegate.isDefaultNamespace(ele)) {14                         parseDefaultElement(ele, delegate);15                     }16                     else {17                         delegate.parseCustomElement(ele);18                     }19                 }20             }21         }22         else {23             delegate.parseCustomElement(root);24         }25     }

第17行,解析自定义的命名空间标签,跟踪方法

//org.springframework.beans.factory.xml.BeanDefinitionParserDelegate  1     public BeanDefinition parseCustomElement(Element ele) { 2         return parseCustomElement(ele, null); 3     } 4  5     public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 6         String namespaceUri = getNamespaceURI(ele); 7         NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 8         if (handler == null) { 9             error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);10             return null;11         }12         return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));13     }

第6行,获取标签的命名空间,第7行,获取命名空间的处理器,第12行,调用命名空间处理器的parse方法解析标签。所有的自定义的命名空间的标签都是按照这种方式进行解析的,下面来看下具体都是怎么实现的

this.readerContext是通过org.springframework.beans.factory.xml.XmlBeanDefinitionReader的createReaderContext方法创建的

//org.springframework.beans.factory.xml.XmlBeanDefinitionReader  1     /** 2      * Create the {
@link XmlReaderContext} to pass over to the document reader. 3 */ 4 public XmlReaderContext createReaderContext(Resource resource) { 5 return new XmlReaderContext(resource, this.problemReporter, this.eventListener, 6 this.sourceExtractor, this, getNamespaceHandlerResolver()); 7 } 8 9 /**10 * Lazily create a default NamespaceHandlerResolver, if not set before.11 * @see #createDefaultNamespaceHandlerResolver()12 */13 public NamespaceHandlerResolver getNamespaceHandlerResolver() {14 if (this.namespaceHandlerResolver == null) {15 this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();16 }17 return this.namespaceHandlerResolver;18 }19 20 /**21 * Create the default implementation of {
@link NamespaceHandlerResolver} used if none is specified.22 * Default implementation returns an instance of {
@link DefaultNamespaceHandlerResolver}.23 */24 protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {25 return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());26 }

根据上边代码我们知道this.readerContext.getNamespaceHandlerResolver()方法返回的是DefaultNamespaceHandlerResolver实例,进入resolve方法

//org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver  1     /** 2      * Locate the {
@link NamespaceHandler} for the supplied namespace URI 3 * from the configured mappings. 4 * @param namespaceUri the relevant namespace URI 5 * @return the located {
@link NamespaceHandler}, or {
@code null} if none found 6 */ 7 @Override 8 public NamespaceHandler resolve(String namespaceUri) { 9 Map
handlerMappings = getHandlerMappings();10 Object handlerOrClassName = handlerMappings.get(namespaceUri);11 if (handlerOrClassName == null) {12 return null;13 }14 else if (handlerOrClassName instanceof NamespaceHandler) {15 return (NamespaceHandler) handlerOrClassName;16 }17 else {18 String className = (String) handlerOrClassName;19 try {20 Class
handlerClass = ClassUtils.forName(className, this.classLoader);21 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {22 throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +23 "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");24 }25 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);26 namespaceHandler.init();27 handlerMappings.put(namespaceUri, namespaceHandler);28 return namespaceHandler;29 }30 catch (ClassNotFoundException ex) {31 throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +32 namespaceUri + "] not found", ex);33 }34 catch (LinkageError err) {35 throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +36 namespaceUri + "]: problem with handler class file or dependent class", err);37 }38 }39 }

关注第24--28行,根据Class对象实例化NamespaceHandler,调用NamespaceHandler实例的init()方法,把命名空间和NamespaceHandler实例映射关系放入handlerMappings,返回NamespaceHandler实例。那么handlerMappings是怎么来的呢,进入getHandlerMappings()方法

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver  1     /** 2      * Load the specified NamespaceHandler mappings lazily. 3      */ 4     private Map
getHandlerMappings() { 5 if (this.handlerMappings == null) { 6 synchronized (this) { 7 if (this.handlerMappings == null) { 8 try { 9 Properties mappings =10 PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);11 if (logger.isDebugEnabled()) {12 logger.debug("Loaded NamespaceHandler mappings: " + mappings);13 }14 Map
handlerMappings = new ConcurrentHashMap
(mappings.size());15 CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);16 this.handlerMappings = handlerMappings;17 }18 catch (IOException ex) {19 throw new IllegalStateException(20 "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);21 }22 }23 }24 }25 return this.handlerMappings;26 }

第9、10行,加载所有Spring的jar文件中的"META-INF/spring.handlers"属性文件,第15行,把属性文件转换成map,第16行,转换后的map赋值到this.handlerMappings。

我们来看看spring-tx-4.0.2.RELEASE.jar和spring-webmvc-4.0.2.RELEASE.jar中spring.handlers文件

从上边两个图中我们可以清楚的看到,每个命名空间都对应命名空间处理器的类全路径。进入org.springframework.transaction.config.TxNamespaceHandler的init方法,看下这个方法都做了那些事情

//org.springframework.transaction.config.TxNamespaceHandler 1     @Override2     public void init() {3         registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());4         registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());5         registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());6     }
//org.springframework.beans.factory.xml.NamespaceHandlerSupport 1     /**2      * Subclasses can call this to register the supplied {
@link BeanDefinitionParser} to3 * handle the specified element. The element name is the local (non-namespace qualified)4 * name.5 */6 protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {7 this.parsers.put(elementName, parser);8 }

注意registerBeanDefinitionParser方法是在父类org.springframework.beans.factory.xml.NamespaceHandlerSupport中。init()主要是注册了advice、annotation-driven、jta-transaction-manager标签对应的解析器。

回到org.springframework.beans.factory.xml.BeanDefinitionParserDelegate的parseCustomElement方法

//org.springframework.beans.factory.xml.BeanDefinitionParserDelegate   1     public BeanDefinition parseCustomElement(Element ele) { 2         return parseCustomElement(ele, null); 3 } 4 5 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 6 String namespaceUri = getNamespaceURI(ele); 7 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 8 if (handler == null) { 9 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 10 return null; 11 } 12 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 13 }

 现在NamespaceHandler实例已经构建完成,同时也执行了init()方法,再来看下parse()方法(在父类org.springframework.beans.factory.xml.NamespaceHandlerSupport中),进入方法

1     /** 2      * Parses the supplied {
@link Element} by delegating to the {
@link BeanDefinitionParser} that is 3 * registered for that {
@link Element}. 4 */ 5 @Override 6 public BeanDefinition parse(Element element, ParserContext parserContext) { 7 return findParserForElement(element, parserContext).parse(element, parserContext); 8 } 9 10 /**11 * Locates the {
@link BeanDefinitionParser} from the register implementations using12 * the local name of the supplied {
@link Element}.13 */14 private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {15 String localName = parserContext.getDelegate().getLocalName(element);16 BeanDefinitionParser parser = this.parsers.get(localName);17 if (parser == null) {18 parserContext.getReaderContext().fatal(19 "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);20 }21 return parser;22 }

第16行,得到的是在org.springframework.transaction.config.TxNamespaceHandler的init中注册的各个标签对应的解析器,<tx:advice/>对应的是TxAdviceBeanDefinitionParser实例,所以第7行调用的parse方法就是TxAdviceBeanDefinitionParser中的parse方法,其它的标签也是调用各自的解析器进行解析的。其它的命名空间也是同样的处理方式。

总结Spring解析自定义标签

1、在BeanDefinitionParserDelegate的parseCustomElement()中调用DefaultNamespaceHandlerResolver的resolve方法

2、resolve方法根据属性文件创建了各个命名空间NameSpaceHandler实例,调用NameSpaceHandler的init()方法注册每个标签所对应的解析器

3、BeanDefinitionParserDelegate的parseCustomElement()中调用DefaultNamespaceHandlerResolver的parse方法

4、parse方法查找第2步注册的解析器,调用其parse方法解析具体的标签

转载于:https://www.cnblogs.com/jintian315/p/8759377.html

你可能感兴趣的文章
nginx的proxy相关配置
查看>>
7 Linux 之正则表达式
查看>>
apache工作模式
查看>>
saltstack部署nginx进阶
查看>>
Junit--参数化测试
查看>>
java 8 CompletableFuture (3)
查看>>
在11.31和11.31之前的HP-UX上如何解读tape设备文件名
查看>>
Extjs grid中的checkbox的选中获取数据是否为最新的问题
查看>>
cocos2d-x与ISO内存管理
查看>>
Linux之获取命令帮助
查看>>
一步、二步, ok!Mac上压缩格式格式已选好!
查看>>
基于skyline的三维地下管网系统
查看>>
react 新手学习笔记1
查看>>
安装CentOS 6.7以及CentOS 7.1
查看>>
域用户自动映射文件服务器分配的空间(只能自己看到,其它域用户无查看权限)...
查看>>
eclipse安装activiti插件和基本使用
查看>>
Linux下的DHCP中继代理配置
查看>>
计算机基本知识(8005)---HDD(硬盘驱动器Hard Disk Drive)
查看>>
什么是云计算
查看>>
ArrayList 和 CopyOnWriteArrayList
查看>>