Spring自定义命名空间的解析原理与实现
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;}// 根据命名空间找到对应的NamespaceHandlerNamespaceHandler 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.orgBUILD 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 {@Overrideprotected Class<?> getBeanClass(Element element) {return User.class;}@Overrideprotected 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 {@Overridepublic 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.xsdhttp://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'}
Spring自定义命名空间的解析原理与实现相关推荐
- Spring自定义命名空间
Spring自定义命名空间提供了一种很好的方式来简化用于描述Spring应用程序上下文的bean定义的xml文件. 这是一个相当古老的概念,最初是在Spring 2.0中引入的,但值得不时地进行审查. ...
- Spring自定义标签使用及原理
最近大半年一直在看spring的源码,寻思着需要写点什么,也锻炼下自己文档编写的能力.本篇我们讲解spring自定义标签的使用及原理,分为以下小节进行讲解. 自定义标签的用途 自定义标签使用 自定义标 ...
- spring MVC使用自定义的参数解析器解析参数
目录 写在前面 编写自定义的参数解析器解析请求参数 项目结构 定义注解 实体类 controller 定义参数解析器 注册参数解析器 启动项目 发起请求查看结果 写在前面 如果还有小伙伴不知道spri ...
- Spring源码深度解析(郝佳)-学习-第二章-容器的基本实现
DefaultListableBeanFactory XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整 ...
- return error怎么定义_这一次搞懂Spring自定义标签以及注解解析原理
自定义标签解析原理 在上一篇分析默认标签解析时看到过这个类DefaultBeanDefinitionDocumentReader的方法parseBeanDefinitions:拉勾IT课小编为大家分解 ...
- Spring源码解析:自定义标签的解析过程
2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...
- 【死磕 Spring】----- IOC 之解析 bean 标签:解析自定义标签
前面四篇文章都是分析 Bean 默认标签的解析过程,包括基本属性.六个子元素(meta.lookup-method.replaced-method.constructor-arg.property.q ...
- Spring源码深度解析(郝佳)-学习-Spring Boot体系原理
Spring Boot是由Pivotal团队提供的全新框架,其设计目的用来简化新Spring应用初始化搭建以及开发过程,该框架使用了我写的方式进行配置,从而开发人员不再需要定义样板化的配置,通过这 ...
- 开发 Spring 自定义视图和视图解析器
Spring 视图和视图解析器简介 什么是 Spring 视图和视图解析器 Spring MVC(Model View Controller)是 Spring 中一个重要的组成部分,而 Spring ...
最新文章
- h5 返回上一页并且刷新页面
- sphinx error connection to 127.0.0.1:9312 failed (errno=0, msg=)
- keepalived 多实例
- 计算机的四个硬盘有什么区别是什么意思,笔记本的内存和硬盘有什么区别 原来笔记本硬盘有这几种...
- 英语总结系列(二十二):Baby偶遇GCT
- Java 连接 SQL Server 数据库
- 测试 JavaScript 函数的性能
- oracle打开dmp文件乱码,oracle中导入dmp字符乱码分析和解决方案
- 高端驱动和低端驱动--ir2110
- <el-link>去掉下划线
- 有开始边DOTA边博客了
- 原来 Elasticsearch 还可以这么理解
- python进阶练习题:IRR计算 - 盈利能力的评价【难度:2级】--景越Python编程实例训练营,不同难度Python习题,适合自学Python的新手进阶
- linux解压时,z x v f分别代表什么意思
- 抖音趣味测试、心理测试类短视频素材哪里找?文案怎么写?技巧大汇总
- IP地址、子网掩码、网关
- 互联网晚报|12/27星期二| ​​国家卫健委:取消入境后全员核酸检测和集中隔离;新冠肺炎更名为新冠感染;知网回应被罚8760万...
- 有趣的灵魂不多,但有趣的设计素材这里很多
- Android 声音分贝控制锁屏demo实现
- 数学之美:激发思考的奥秘