上一章我们介绍了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方法解析具体的标签