一 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>標(biāo)簽
this.parseDefaultElement(ele, delegate);
} else {
//非<beans>標(biāo)簽
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
進(jìn)入 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>標(biāo)簽外所有的類定義的加載都會有一個對應(yīng)的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.根據(jù)namespaceUri獲取一個NamespaceHandler實例handler椰拒。
- 讓handler解析出我們想要的BeanDefinition.
先看如何獲取一個標(biāo)簽對應(yīng)的namespaceUri,以spring標(biāo)簽 <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/> 對應(yīng)的namespaceUri即為對應(yīng)的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);
}
}
}
整個方法并不復(fù)雜便瑟,在解析整個方法之前缆毁,我們先看下該類的兩個構(gòu)造方法:
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;
}
看到了嗎,這里終于出現(xiàn)了我們這篇文章的主角"META-INF/spring.handlers"到涂,spring默認(rèn)就是取這個路徑下的配置信息脊框。下面是spring-aop包下的META-INF/spring.handlers文件中的內(nèi)容
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中浇雹,如若是第一次調(diào)用,會加載屿讽,后面的調(diào)用都是直接返回實例屬性handlerMappings昭灵。
- 后面的邏輯主要就是一個if --else if--else 結(jié)構(gòu)。 handlerMappings中一開始存放的都是對應(yīng)的namespaceHandler的全路徑名(String類型),如果是第一次獲取這個handler烂完,必然會走else分之试疙。
這里也就是一個簡單的懶加載邏輯,因為并不是所有的META-INF/spring.handlers中的handler項目中都會用到窜护,所以也就沒必要實例化它們效斑,直到第一次調(diào)用的時候再去實例化。實例化邏輯不再具體分析柱徙。
整個handler的獲取是很簡單的邏輯缓屠。