spring类的加载之META-INF/spring.handlers

一 spring之META-INF/spring.handlers
在springboot 项目中通常都有一个application.xml的配置文件。
其中的配置解析一个核心步骤如下:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element)node;
                    if (delegate.isDefaultNamespace(ele)) {
                       //<beans>标签
                        this.parseDefaultElement(ele, delegate);
                    } else {
                      //非<beans>标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

进入 delegate.parseCustomElement(ele)方法。在类BeanDefinitionParserDelegate中。

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        String namespaceUri = this.getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        } else {
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }

其中bean定义都是交由NamespaceHandler 来解析。也就是说除了<beans>标签外所有的类定义的加载都会有一个对应的NameSpaceHanlder来处理具体的BeanDefinition的获取。
NamespaceHandler是一个接口:

public interface NamespaceHandler {
    void init();
    BeanDefinition parse(Element var1, ParserContext var2);

    BeanDefinitionHolder decorate(Node var1, BeanDefinitionHolder var2, ParserContext var3);
}

回到BeanDefinitionParserDelegate.parseCustomElement(ele)方法中,这个方法主要分为三步骤
1.获取一个namespaceUri,
2.根据namespaceUri获取一个NamespaceHandler实例handler。

  1. 让handler解析出我们想要的BeanDefinition.

先看如何获取一个标签对应的namespaceUri,以spring标签 <context:annotation-config/>为例。
我们的application.xml都会有形如

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
       default-lazy-init="false">

这样的头部
<context:annotation-config/> 对应的namespaceUri即为对应的http://www.springframework.org/schema/context

让我们来到第二步handler的获取,在DefaultNamespaceHandlerResolver.resolve(String namespaceUri)方法中。

    public NamespaceHandler resolve(String namespaceUri) {
        Map<String, Object> handlerMappings = this.getHandlerMappings();
        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");
                } else {
                    NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
                    namespaceHandler.init();
                    handlerMappings.put(namespaceUri, namespaceHandler);
                    return namespaceHandler;
                }
            } catch (ClassNotFoundException var7) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
            } catch (LinkageError var8) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
            }
        }
    }

整个方法并不复杂,在解析整个方法之前,我们先看下该类的两个构造方法:

    public DefaultNamespaceHandlerResolver() {
        this((ClassLoader)null, "META-INF/spring.handlers");
    }
    public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
        this(classLoader, "META-INF/spring.handlers");
    }
    public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
        this.logger = LogFactory.getLog(this.getClass());
        Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
        this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader();
//  "META-INF/spring.handlers"     
 this.handlerMappingsLocation = handlerMappingsLocation;
    }

看到了吗,这里终于出现了我们这篇文章的主角"META-INF/spring.handlers",spring默认就是取这个路径下的配置信息。下面是spring-aop包下的META-INF/spring.handlers文件中的内容

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

回到DefaultNamespaceHandlerResolver.resolve(String namespaceUri)方法。

  1. Map<String, Object> handlerMappings = this.getHandlerMappings();方法会返回所有的META-INF/spring.handlers中的所有的配置信息,这个方法保存在实例属性handlerMappings中,如若是第一次调用,会加载,后面的调用都是直接返回实例属性handlerMappings。
  2. 后面的逻辑主要就是一个if --else if--else 结构。 handlerMappings中一开始存放的都是对应的namespaceHandler的全路径名(String类型),如果是第一次获取这个handler,必然会走else分之。
    这里也就是一个简单的懒加载逻辑,因为并不是所有的META-INF/spring.handlers中的handler项目中都会用到,所以也就没必要实例化它们,直到第一次调用的时候再去实例化。实例化逻辑不再具体分析。
    整个handler的获取是很简单的逻辑。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容