文章目录
Spring源码系列:
前言
正文
方法1:parseCustomElement
方法2:getNamespaceHandlerResolver().resolve
方法3:getHandlerMappings
方法4:namespaceHandler.init()
方法5:handler.parse
自定义命名空间
1、新建自定义命名空间处理器
2、新建自定义命名空间解析器
3、新建spring.handlers文件
4、新建spring.schemas文件
5、新建xsd文件,声明标签规范
6、新建xml文件配置自定义标签
6、测试用例
总结
Spring源码系列:
Spring IOC源码:简单易懂的Spring IOC 思路介绍
Spring IOC源码:核心流程介绍
Spring IOC源码:ApplicationContext刷新前准备工作
Spring IOC源码:obtainFreshBeanFactory 详解(上)
Spring IOC源码:obtainFreshBeanFactory 详解(中)
Spring IOC源码:obtainFreshBeanFactory 详解(下)
Spring IOC源码:<context:component-scan>源码详解
Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解
Spring IOC源码:registerBeanPostProcessors 详解
Spring IOC源码:实例化前的准备工作
Spring IOC源码:finishBeanFactoryInitialization详解
Spring IoC源码:getBean 详解
Spring IoC源码:createBean( 上)
Spring IoC源码:createBean( 中)
Spring IoC源码:createBean( 下)
Spring IoC源码:finishRefresh 完成刷新详解
前言
前面两篇文章介绍了BeanDefinition的解析,以及默认命名空间的解析过程,这节介绍obtainFreshBeanFactory中另一个核心的解析逻辑,自定义命名空间的解析。
正文
自定义命名空间解析,入口方法为delegate.parseCustomElement(ele),见方法1详解
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); } }
方法1:parseCustomElement
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } /** * Parse a custom element (outside of the default namespace). * @param ele the element to parse * @param containingBd the containing bean definition (if any) * @return the resulting bean definition */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { //获取命名空间路径 例如:http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } //通过命名空间解析器加载命名空间处理器 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)); }
this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)方法,见方法3详解
handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法,见方法5详解
方法2:getNamespaceHandlerResolver().resolve
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 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("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } } }
getHandlerMappings(),见方法3详解
namespaceHandler.init(),见方法4详解
方法3:getHandlerMappings
private Map<String, Object> getHandlerMappings() { //获取当前集合中的处理器 Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { //根据处理器路径进行查找加载 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap<>(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 handlerMappings; }
handlerMappings集合中的处理器如下图:
方法4:namespaceHandler.init()
因为当前配置文件中的自定义命名空间是AOP,所以这里会进入AOP处理器的init方法。
public void init() { //这里主要注册需要用到的解析器 // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
方法5:handler.parse
进入方法1中的handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法;
public BeanDefinition parse(Element element, ParserContext parserContext) { //根据当前节点的标签去查找对应的解析器 BeanDefinitionParser parser = findParserForElement(element, parserContext); //调用解析器处理逻辑 return (parser != null ? parser.parse(element, parserContext) : null); } /** * Locates the {@link BeanDefinitionParser} from the register implementations using * the local name of the supplied {@link Element}. */ @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { //获取节点标签 如:config 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; }
到此整个obtainFreshBeanFactory 的讲解就结束了,自定义命名空间处理器及其解析方法,后续文章中会跟案例进行讲解。
自定义命名空间
看完上面的步骤,大家应该对自定义命名空间过程有一定的理解,下面我们自定义案例来加深理解,我们需要按以下步骤来完成Demo。
1、新建自定义命名空间处理器,一个处理器可以添加多个解析器
2、新建自定义命名空间解析器,编写解析处理逻辑
3、新建spring.handlers文件,按Key-Value格式指定处理器路径
4、新建spring.schemas文件,按Key-Value格式指定xsd自定义标签文件路径
5、新建xsd文件,声明标签规范,指定命名空间处理器
6、新建xml文件配置自定义标签,完成类的管理。
1、新建自定义命名空间处理器
我们新建一个空模块Gradle空模块
编辑build.gradle文件,引入spring 包
新建处理器类 ZdcHandler
package com.zhudachang.namespace; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class ZdcHandler extends NamespaceHandlerSupport { @Override public void init() { super.registerBeanDefinitionParser("zdc",new ZdcParser()); } }
2、新建自定义命名空间解析器
package com.zhudachang.namespace; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; /** * Zdc解析器 */ public class ZdcParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(ZdcDomain.class); //声明PropertyValues对象 MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues(); // 添加name属性 if (element.hasAttribute("name")) { mutablePropertyValues.addPropertyValue("name", element.getAttribute("name")); } // 解析添加remark属性 if (element.hasAttribute("remark")) { mutablePropertyValues.addPropertyValue("remark", element.getAttribute("remark")); } String id = element.getAttribute("id"); // 拿到注册表, 注册BeanDefinition parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); return beanDefinition; } }
新建实体类
package com.zhudachang.namespace; public class ZdcDomain { String name; String remark; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
3、新建spring.handlers文件
resource目录下创建META-INF目录,并创建spring.handlers文件,指定空间路径及其对应的处理器类路径
http\://com.zhudachang.namespace/schema/zdc=com.zhudachang.namespace.ZdcHandler
4、新建spring.schemas文件
resource目录下创建META-INF目录,并创建spring.schemas文件,指定路径及其对应xsd文件路径
http\://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd=./com/zdc/spancename/zdc/config/zdc-1.0.xsd
5、新建xsd文件,声明标签规范
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://com.zhudachang.namespace/schema/zdc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://com.zhudachang.namespace/schema/zdc" elementFormDefault="qualified"> <xsd:element name="zdc" > <xsd:complexType> <xsd:attribute name="id" type="xsd:string"/> <xsd:attribute name="name" type="xsd:string"/> <xsd:attribute name="remark" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema>
6、新建xml文件配置自定义标签
如果你的测试模块跟上述定义解析器标签不在同个模块下,记得先引入模块。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:zdc="http://com.zhudachang.namespace/schema/zdc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://com.zhudachang.namespace/schema/zdc http://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd"> <zdc:zdc id="zdc" name="猪大肠" remark="猪大肠备注"></zdc:zdc> </beans>
6、测试用例
编写测试用例
public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("application-namespace.xml"); ZdcDomain zdcDomain = (ZdcDomain) applicationContext.getBean("zdc"); System.out.println(zdcDomain.getName()); }
测试结果:
总结
本篇文章介绍了自定义命名空间的解析过程,并且编写的自定义命名空间案例加深理解。后续文章中会拿常用的自定义标签context:component-scan 进行讲解。