閱讀須知
- Spring源碼版本:4.3.8
- 文章中使用注釋的方法會做深入分析
正文
上篇文章我們介紹了Spring預設标簽的解析,本文我們來分析一下Spring自定義标簽的解析。上篇文章我們了解到Spring的預設标簽目前有4個(import、alias、bean、beans),也就是說除了這4個标簽以外的标簽都是自定義标簽(當然這裡所說的标簽不包括那些以子标簽形式存在的如property、value等标簽),如我們熟知的事務标簽
<tx:annotation-driven/>
、注解掃描标簽
<context:component-scan/>
等都屬于自定義标簽,下面就讓我們來分析一下這些自定義标簽是如何解析的,承接Spring源碼解析之預設标簽的解析文中自定義标簽解析的分支:
BeanDefinitionParserDelegate:
public BeanDefinition parseCustomElement(Element ele) {
/* 解析自定義元素 */
return parseCustomElement(ele, null);
}
BeanDefinitionParserDelegate:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 擷取命名空間
String namespaceUri = getNamespaceURI(ele);
/* 根據命名空間擷取NamespaceHandler */
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
/* 解析自定義标簽 */
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
DefaultNamespaceHandlerResolver:
public NamespaceHandler resolve(String namespaceUri) {
/* 擷取所有已經配置的handler映射 */
Map<String, Object> handlerMappings = 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");
}
// 執行個體化對象
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init(); // 調用init方法
// 放入緩存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
DefaultNamespaceHandlerResolver:
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
// 加載配置
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
// 放入緩存
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}
這裡就是加載配置檔案到記憶體中,而
this.handlerMappingsLocation
在調用構造方法初始化時被指派為META-INF/Spring.handlers,我們來看一下Spring事務自定義标簽對應這裡的配置,鎖定spring-tx包下的META-INF/Spring.handlers:
http\:// www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
是以在解析Spring事務自定義标簽時就會找到TxNamespaceHandler并調用其init方法:
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
init方法就是注冊對應三個标簽的解析器,這裡用Spring事務标簽舉例,其他自定義标簽大同小異。下面就是解析過程:
NamespaceHandlerSupport:
public BeanDefinition parse(Element element, ParserContext parserContext) {
/* 尋找解析器進行解析 */
return findParserForElement(element, parserContext).parse(element, parserContext);
}
NamespaceHandlerSupport:
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 以<tx:annotation-driven/>為例,這裡的localName就是annotation-driven
String localName = parserContext.getDelegate().getLocalName(element);
// 根據localName擷取解析器,上面注冊過annotation-driven的解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
擷取到BeanDefinitionParser後,調用其parse方法進行解析,解析的過程就是根據開發者需求的不同自行實作了,整體來說就是解析配置到注冊BeanDefinition的過程。到這裡自定義标簽的解析就完成了。