Spring自定义命名空间的解析原理与实现
原理
由上篇文章refresh() -> obtainFreshBeanFactory()跟踪源码可知Spring在解析除默认命名空间import、alias、bean、beans
以外的命名空间都会调用BeanDefinitionParserDelegate的**BeanDefinition parseCustomElement(Element ele)**方法进行解析
BeanDefinitionParserDelegate -> parseCustomElement()
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取对应的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据命名空间找到对应的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));
}
DefaultNamespaceHandlerResolver -> resolve():获取handler的方法
在resolve()
中可识别找到自定义的NamespaceHandler
对象
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的初始化方法
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);
}
}
}
NamespaceHandlerSupport -> parse():找到对应的handler进行解析
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
通常调用的为AbstractBeanDefinitionParser -> parse()
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 主要实现自定义解析方法
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 将AbstractBeanDefinition转换为BeanDefinitionHolder并注册
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
// 通知监听器进行处理
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
AbstractSingleBeanDefinitionParser -> parseInternal() 其中 doParse() 为自定义实现解析
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的class,此时会调用自定义解析器
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// 若子类没有重写getBeanClass方法则尝试检查子类是否重写getBeanClassName方法
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// Inner bean definition must receive same scope as containing bean.
// 若存在父类则使用父类的scope属性
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
// 配置延迟加载
builder.setLazyInit(true);
}
// 调用子类重写的doParse方法进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
AbstractSingleBeanDefinitionParser -> doParse() 调用子类重写的doParse方法进行解析
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}
// 实际执行(什么也没做,预示子类实现自行扩展功能)
protected void doParse(Element element, BeanDefinitionBuilder builder) {
}
实现
-
自定义
xsd
文件,模仿spring
中的xsd
文件进行自定义xsd
文件编写 -
自定义实体类定义属性对应
xsd
中对应的自定义标签 -
自定义
BeanDefinitionParser
类继承org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
重写Class<?> getBeanClass(Element element)
:返回自定义实体的class
对象void doParse(Element element, BeanDefinitionBuilder builder)
:实现重文档Element
中获取参数存入BeanDefinitionBuilder
中
-
自定义
NamespaceHandler
处理类继承org.springframework.beans.factory.xml.NamespaceHandlerSupport
重写init()
:调用void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser)
将具体的自定义标签名与自定义的解析转换对象以kv形式存入map集合
-
在
resource
目录下新建META-INF
文件夹 -
在
META-INF
文件夹中新增-
spring.handlers
文件:模仿spring中的handler文件 -
spring.schemas
文件:模仿spring中的schemas文件在自定义时可能
spring.handlers/spring.schemas
这种写法可能会抛出异常,将s
改为S
即可
异常信息FAILURE: Build failed with an exception. * Where: Script '/Users/armin/IdeaProjects/spring-framework/gradle/docs.gradle' line: 228 * What went wrong: A problem occurred evaluating script. > assert shortName != key | | | | | 'http://www.armin.com/schema/user.xsd' | false 'http://www.armin.com/schema/user.xsd' * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 659ms
或如以拉下源码在源码中找到
spring-framework
->gradle
->docs.gradle
中注释以下代码// for (def key : schemas.keySet()) { // def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') // assert shortName != key // File xsdFile = module.sourceSets.main.resources.find { // (it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\'))) // } // assert xsdFile != null // into (shortName) { // from xsdFile.path // } // }
-
-
使用时在xml文件中像引入其他命名空间一样引入即可使用
1.自定义xsd文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.armin.com/schema/user"
xmlns:tns="http://www.armin.com/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name ="id" type = "string"/>
<attribute name ="username" type = "string"/>
<attribute name ="email" type = "string"/>
<attribute name ="age" type="string"/>
</complexType>
</element>
</schema>
2.自定义实体类定义接收属性
@Data
public class User {
private String username;
private String email;
private String age;
}
3.自定义parse对象
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String username = element.getAttribute("username");
String email = element.getAttribute("email");
String age = element.getAttribute("age");
if (StringUtils.hasText(username)) {
builder.addPropertyValue("username", username);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
if (StringUtils.hasText(age)) {
builder.addPropertyValue("age", age);
}
}
}
4.自定义NamespaceHandler对象
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
5.resource文件夹新建META-INF文件夹
6.新建spring.handlers与spring.schemas文件
spring.handlers
http\://www.armin.com/schema/user=com.armin.selftag.UserNamespaceHandler
spring.schemas
http\://www.armin.com/schema/user.xsd=META-INF/user.xsd
7.在xml文件中引入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:armin="http://www.armin.com/schema/user"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.armin.com/schema/user http://www.armin.com/schema/user.xsd
">
<armin:user id="armin" username="enzo" email="enzo@armin.com" age="18"/>
</beans>
测试
import com.armin.selftag.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User armin = (User) context.getBean("armin");
System.out.println(armin);
}
}
测试结果
> Task :spring-debug:Test.main()
User{username='enzo', email='enzo@armin.com', age='18'}