Spring 源码解析(七)解析自定义命名空间的标签

上一章我们介绍了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文件

从上边两个图中我们可以清楚的看到,每个命名空间都对应命名空间处理器的类全路径。进入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 }

 现在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 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方法解析具体的标签

posted @ 2018-04-09 14:03  jintian315  阅读(1201)  评论(0编辑  收藏  举报