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);
}
}
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详解
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详解
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;
}
因为当前配置文件中的自定义命名空间是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());
}
进入方法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文件配置自定义标签,完成类的管理。
我们新建一个空模块Gradle空模块
新建处理器类 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());
}
}
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;
}
}
resource目录下创建META-INF目录,并创建spring.handlers文件,指定空间路径及其对应的处理器类路径
http\://com.zhudachang.namespace/schema/zdc=com.zhudachang.namespace.ZdcHandler
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
<?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>
如果你的测试模块跟上述定义解析器标签不在同个模块下,记得先引入模块。
<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>
编写测试用例
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 进行讲解。