一 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。
- 让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)方法。
- Map<String, Object> handlerMappings = this.getHandlerMappings();方法会返回所有的META-INF/spring.handlers中的所有的配置信息,这个方法保存在实例属性handlerMappings中,如若是第一次调用,会加载,后面的调用都是直接返回实例属性handlerMappings。
- 后面的逻辑主要就是一个if --else if--else 结构。 handlerMappings中一开始存放的都是对应的namespaceHandler的全路径名(String类型),如果是第一次获取这个handler,必然会走else分之。
这里也就是一个简单的懒加载逻辑,因为并不是所有的META-INF/spring.handlers中的handler项目中都会用到,所以也就没必要实例化它们,直到第一次调用的时候再去实例化。实例化逻辑不再具体分析。
整个handler的获取是很简单的逻辑。