Spring
除了实现了默认的标签解析,如
,还提供了自定义的标签解析机制,由用户实现解析自定义标签的逻辑,如 Spring
实现的自定义标签:
等。
本篇博客主要关注自定义标签解析的相关逻辑,Spring
解析标签前准备工作、解析默认标签,可参看我的其他博客。
Spring
解析 bean 属性的实现逻辑 DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
中,会判断元素是否是默认的命名空间 isDefaultNamespace(Element)
,即 http://www.springframework.org/schema/beans
:
parseDefaultElement(ele, delegate);
delegate.parseCustomElement(ele);
入口如下:
/**
* 解析 bean 属性:
* 1.默认标签: import, beans, bean, alias
* 2.自定义标签: aop, context, tx 等
*/
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
解析自定义标签 BeanDefinitionParserDelegate#parseCustomElement(Element)
的实现如下:
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
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));
}
从上面的代码,可以看出解析自定义标签的步骤主要为:
namespaceUri
namespaceUri
获取命名空间处理器 NamespaceHandler
NamespaceHandler#parse
方法解析标签信息对应的时序图如下:
获取命名空间资源标识 getNamespaceUri(Element)
不过多介绍,接下来就详细介绍获取命名空间解析器和解析自定义标签两部分。
获取解析器的逻辑在 DefaultNamespaceHandlerResolver#resolve(String namespaceUri)
中:
/**
* 获取 NamespaceHandler
*/
public NamespaceHandler resolve(String namespaceUri) {
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();
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);
}
}
}
主要分为以下步骤:
META-INF/spring.handlers
namespaceUri
获取处理器名称NamespaceHandler#init
执行初始化逻辑首先,看下 NamespaceHandler
的类图:
NamespaceHandler#init
方法初始化命名空间解析器,NamespaceHandler#parse
方法解析具体的命名空间标签元素。
NamespaceHandlerSupport
为命名空间解析器辅助类,registerBeanDefinitionParser(elementName, beanDefinitionParser)
方法将标签和 bean 属性解析器 BeanDefinitionParser
的映射关系注册到 parsers
中。
Spring
常见的命名空间解析器有:
AopNamespaceHandler
:AOP 命名空间
ContextNamespaceHandler
:上下文命名空间
TxNamespaceHandler
:事务命名空间
接着看 DefaultNamespaceHandlerResolver#getHandlerMappings
部分:
/**
* 默认解析路径,会查询所有的 jar 文件
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
/**
* 加载 namespaceUri 对应的 NamespaceHandler 信息,懒加载
*/
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;
}
默认从 META-INF/spring.handlers
位置加载配置信息,key-value
方式。
以 spring-context
模块的命名空间解析器映射关系配置为例:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
获取到对应的处理器名称之后,先通过反射实例化,再调用 NamespaceHandler#init
方法执行初始化逻辑。
以命名空间 aop
的解析器 AopNamespaceHandler
为例:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
// 命名空间解析器初始化
@Override
public void init() {
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
// 注册 bean 属性解析器
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
}
Spring AOP
实现了 AspectJAutoProxyBeanDefinitionParser
类来解析 aspectj-autoproxy
标签,实现了 ScopedProxyBeanDefinitionDecorator
类来解析 scoped-proxy
标签。
接下来是解析逻辑 NamespaceHandlerSupport#parse
的实现:
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
这里的实现比较简单,从 parses
中获取 bean 属性解析器 BeanDefinitionParser
对象,然后调用 BeanDefinitionParser#parse
方法。
以上,就是 Spring
自定义标签解析机制的逻辑,主要在于 NamespaceHandler
和 BeanDefinitionParser
两个接口的实现。