写点什么

Spring XML Schema 扩展机制源码解读

用户头像
Hex
关注
发布于: 2021 年 05 月 30 日

聊聊 Spring 的 XML Schema 扩展机制的使用方式中介绍了如何通过 xml 自定义标签进行 spring 扩展,在各类组件(Redis,Mybatis,Dubbo)需要和 Spring 进行整合使用时都会用到。


本文将从 Spring 源码中寻找 xml 自定义标签生效的原因,实际上,在源码中可以发现 Spring 将 xml 标签分成了两类,一类是 spring 自定义的标签<beans>;另一类则是扩展自定义标签,如 spring 事务标签<tx:xxx>等,以及前面文章中介绍的用户自定义的标签都属于扩展自定义标签。


阅读本文前建议可先查看聊聊 Spring 的 XML Schema 扩展机制的使用方式

本文中 spring 源码版本为 4.3.13-RELEASE

源码解析

从容器启动作为入口,如下所示启动代码为聊聊 Spring 的 XML Schema 扩展机制的使用方式一文中的启动代码


public class App {    public static void main(String[] args) {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-service.xml");        DistributedIdComponent bean = context.getBean(DistributedIdComponent.class);
String id = bean.generateId();
System.out.println("id:" + id); }}
复制代码


第一行代码ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-service.xml") 即容器初始化,包括解析 xml、加载 bean 等,ClassPathXmlApplicationContext是 BeanFactory 的实现类。

ClassPathXmlApplicationContext

以 ClassPathXmlApplicationContext 的构造方法为入口,如下:


public ClassPathXmlApplicationContext(String configLocation) throws BeansException {    this(new String[] {configLocation}, true, null);  }
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
复制代码


由于整个过程涉及到的源码层级较深,类较多,为了方法来回查看大致做了如下一个时序图:


实际上到 DefaultBeanDefinitionDocumentReader 类往后涉及到具体的从 xml 标签解析成 BeanDefinition 还有一些步骤没有在上时序图中继续展开,下面会从 parseCustomElement 往后的流程进行详细介绍。


为方便了解上图中给类与类之间的关系,可查看如下 UML 类图:



DefaultBeanDefinitionDocumentReader

如上类图中所示DefaultBeanDefinitionDocumentReader是接口BeanDefinitionDocumentReader的实现类,用于从 XML 中读取 bean definition。


如下源码所示,在doRegisterBeanDefinitions方法中要注意两个地方,一个是创建了一个委派类 BeanDefinitionParserDelegate;一个是 parseBeanDefinitions(root, this.delegate),为具体解析 BeanDefinition 的方法。


  // DefaultBeanDefinitionDocumentReader.java    /**   * Register each bean definition within the given root {@code <beans/>} element.   */  protected void doRegisterBeanDefinitions(Element root) {    // Any nested <beans> elements will cause recursion in this method. In    // order to propagate and preserve <beans> default-* attributes correctly,    // keep track of the current (parent) delegate, which may be null. Create    // the new (child) delegate with a reference to the parent for fallback purposes,    // then ultimately reset this.delegate back to its original (parent) reference.    // this behavior emulates a stack of delegates without actually necessitating one.    BeanDefinitionParserDelegate parent = this.delegate;    this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
preProcessXml(root); // 解析xml中的BeanDefinition parseBeanDefinitions(root, this.delegate); postProcessXml(root);
this.delegate = parent; }
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ 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); } }
复制代码


从 parseBeanDefinitions 方法中的逻辑可以看出,最终会有两个分支,一个是解析默认标签;一个是解析自定义(扩展)标签,这里直接看 parseCustomElement:


  // BeanDefinitionParserDelegate.java  
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)); }
复制代码


关键的一行代码为NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri),获取对应的 NamespaceHandler 类。


其中this.readerContext是在 XmlBeanDefinitionReader 中 registerBeanDefinitions 的方法中实例化 XmlReaderContext 然后传递下来的(可查看第一张时序图来定位到此方法在整个流程中的位置),如下:


// XmlBeanDefinitionReader.java
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); // createReaderContext见下面的方法 documentReader.registerBeanDefinitions(doc, createReaderContext见下面的方法(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
// 实例化XmlReaderContextpublic XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver());}
// 创建DefaultNamespaceHandlerResolverpublic NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; }
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader()); }
复制代码


从而可以得出this.readerContext.getNamespaceHandlerResolver()返回的就是DefaultNamespaceHandlerResolver实例,进而查看DefaultNamespaceHandlerResolver的 resolve(namespaceUri)方法,


 // DefaultNamespaceHandlerResolver.java 
@Override 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); } } }
/** * Load the specified NamespaceHandler mappings lazily. */ 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; }
复制代码


handlerMappingsLocation 默认取META-INF/spring.handlers,通过上述的 getHandlerMappings()方法将META-INF/spring.handlers里配置的信息以 map 的结构保存在handlerMappings中,例如 spring-aop 的 spring-handlers 文件中配置如下:


http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
复制代码


在此处会将http://www.springframework.org/schema/aop作为 map 的 key,org.springframework.aop.config.AopNamespaceHandler 为对应的 value。


因此,回到 BeanDefinitionParserDelegate 的 parseCustomElement 方法中,


// BeanDefinitionParserDelegate.java  
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); // this.readerContext.getNamespaceHandlerResolver()对应为DefaultNamespaceHandlerResolver // 通过DefaultNamespaceHandlerResolver的resolve方法,会从META-INF/spring.handlers中的配置获取到对应的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; } // 使用自定义NamespaceHandler实现类进行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
复制代码


一般通过继承抽象类NamespaceHandlerSupportNamespaceHandlerSupport继承自接口NamespaceHandler)来通过 registerBeanDefinitionParser 注册一个自定义的 BeanDefinitionParser。然后上述


handler.parse 会先从注册过的中找到对应 uri 对应的 BeanDefinitionParser,最后进行解析成 BeanDefinition,


以下以 AopNamespaceHandler 为例进行说明:


// 1.注册BeanDefinitionParser
public class AopNamespaceHandler extends NamespaceHandlerSupport { public AopNamespaceHandler() { }
public void init() { // 调用NamespaceHandlerSupport的registerBeanDefinitionParser进行注册BeanDefinitionParser this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }}
// abstract class NamespaceHandlerSupport// parsers属性保存映射关系private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();
// 上面init方法调用时写入属性parsers中protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }
复制代码


完成了 BeanDefinitionParser 的注册和映射关系保存,则方便后续的解析:


// abstract class NamespaceHandlerSupport
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { // 获取到BeanDefinitionParser,并调用其parse方法 return findParserForElement(element, parserContext).parse(element, parserContext); }
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // 获取名称如aspectj-autoproxy String localName = parserContext.getDelegate().getLocalName(element); // 获取到对应的BeanDefinitionParser 如AspectJAutoProxyBeanDefinitionParser BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }
复制代码


至此,对 spring 容器是如何加载并解析自定义标签的过程基本分析完成。

总结

通过对 spring 容器是如何加载并解析自定义标签的过程的源码过程进行阅读分析,了解自定义标签生效的原因,相应的 spring 是如何对 xml 的配置进行解析为 bean definition 的过程也大致可以了解。上述整个过程完成是跟着 spring 源码调用层级展开,没有具体了解源码为什么是这样进行设计,比如过程中有用到设计模式委派模式等,后续可在此基础上进行进一步的学习。

发布于: 2021 年 05 月 30 日阅读数: 206
用户头像

Hex

关注

还未添加个人签名 2018.05.24 加入

还未添加个人简介

评论

发布
暂无评论
Spring XML Schema扩展机制源码解读