上一章我们介绍了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<String, Object> 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<String, Object> 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<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(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文件

spring默认nacos命名空间修改 spring自定义命名空间_命名空间

spring默认nacos命名空间修改 spring自定义命名空间_spring默认nacos命名空间修改_02

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

//org.springframework.transaction.config.TxNamespaceHandler
1     @Override
2     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} to
3      * 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     }

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 using
12      * 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方法解析具体的标签