Spring的XML解析原理,这一次全搞懂再走( 五 )


public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {BeanDefinitionHolder finalDefinition = definitionHolder;//根据bean标签属性装饰BeanDefinitionHolder , 比如NamedNodeMap attributes = ele.getAttributes();for (int i = 0; i < attributes.getLength(); i++) {Node node = attributes.item(i);finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);}//根据bean标签子元素装饰BeanDefinitionHolder\NodeList children = ele.getChildNodes();for (int i = 0; i < children.getLength(); i++) {Node node = children.item(i);if (node.getNodeType() == Node.ELEMENT_NODE) {finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);}}return finalDefinition; }在这个方法中分别对Bean标签的属性和子标签迭代 , 获取其中的自定义标签进行解析 , 并装饰之前创建的BeanDefinition对象 , 如同下面的c和p:
// c:和p:表示通过构造器和属性的setter方法给属性赋值 , 是constructor-arg和property的简化写法两个步骤是一样的 , 我们点进decorateIfRequired方法中:
public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {//根据node获取到node的命名空间 , 形如:String namespaceUri = getNamespaceURI(node);if (namespaceUri != nullif (handler != null) {//调用NamespaceHandler处理类的decorate方法 , 开始具体装饰过程 , 并返回装饰完的对象BeanDefinitionHolder decorated =handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));if (decorated != null) {return decorated;}}else if (namespaceUri.startsWith("")) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);}else {// A custom namespace, not to be handled by Spring - maybe "xml:...".if (logger.isDebugEnabled()) {logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");}}}return originalDef; }这里也和我们之前说的一样 , 首先获取到标签对应的namespaceUri , 然后通过这个Uri去获取到对应的NamespceHandler , 最后再调用NamespceHandler的decorate方法进行装饰 。 我们先来看看获取NamespceHandler的过程 , 这涉及到一个非常重要的高扩展性的思想——SPI(有关SPI , 在我之前的文章Dubbo——SPI及自适应扩展原理中已经详细讲解过 , 这里不再赘述):
public NamespaceHandler resolve(String namespaceUri) {// 获取spring中所有jar包里面的 "META-INF/spring.handlers"文件 , 并且建立映射关系Map handlerMappings = getHandlerMappings();//根据namespaceUri: , 获取到这个命名空间的处理类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);if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");}NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//调用处理类的init方法 , 在init方法中完成标签元素解析类的注册namespaceHandler.init();handlerMappings.put(namespaceUri, namespaceHandler);return namespaceHandler;}} }// AOP标签对应的NamespaceHandler , 可以发现NamespaceHandler的作用就是管理和注册与自己相关的标签解析器 public void init() {// In 2.0 XSD as well as in 2.1 XSD.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());// Only in 2.0 XSD: moved to context namespace as of 2.1registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }看到这里我们应该就清楚了Spring是如何解析xml里的标签了以及我们如果要扩展自己的标签该怎么做 。 只需要创建一个我们的自定义标签和解析类 , 并指定它的命名空间以及NamespaceHandler , 最后在META-INF/spring.handlers文件中指定命名空间和NamespaceHandler的映射关系即可 , 就像Spring的c和p标签一样:
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandlerhttp\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler像这样使用SPI的思想设计我们的项目的话 , 当需要扩展时 , 不需要改动任何的代码 , 非常的方便优雅 。 接着 , 我们回到handler的decorate方法 , 这里有三个默认的实现类:NamespaceHandlerSupport、SimpleConstructorNamespaceHandler、SimplePropertyNamespaceHandler 。 第一个是一个抽象类 , 与我们这里的流程无关 , 感兴趣的可自行了解 , 第二个和第三个则分别是c和p标签对应的NamespaceHandler , 两个装饰的处理逻辑基本上是一样的 , 我这里进入的是SimpleConstructorNamespaceHandler类: