04-Spring自定义标签解析
自定义标签的解析
这一篇主要说明自定义标签的解析流程,除了 bean、alias、import、beans之外的标签,都属于自定义标签的范围,自定义标签的解析需要命名空间配合,
- 获取对应的命名空间
- 根据命名空间获取对应的命名空间处理器 handler
- 通过对应的 handler 去找到对应的解析器
- 通过对应的解析器去对自定义标签进行解析。
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);
}
}
parseCustomElement(ele, null)
获取对应的命名空间,根据命名空间找到对应的命名空间处理器 handler,通过对应的 handler 去找到对应的解析器,最后使用解析器对标签进行对应的解析
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取对应的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据命名空间找到对应的NamespaceHandlerspring
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));
}
resolve
从命名空间中解析出对应的Handler,然后拿到对应的解析器对其进行解析。
getHandlerMappings() 中的数据来源于在 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)) 中 createReaderContext 配置的默认值,会去对应的位置直接加载对应的文件,在此时进行解析。
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);
}
}
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
BeanDefinitionParser#parse
不同的标签有不同的处理类,具体的处理逻辑不尽相同,最终都会将其解析为 BeanDefinition 子类对象,将其注入到 beandefinitionMap 和 beanDefinitionNames 集合中去。
AnnotationConfigUtils.registerAnnotationConfigProcessors
在某些步骤会注入一些实现 BeanFactoryPostProcessor 接口的子类,后续在解析一些注解的时候会使用到。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 获取beanFactory
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
// //设置依赖比较器
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
// //设置自动装配解析器
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
// 创建BeanDefinitionHolder集合
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 注册内部管理的用于处理@configuration注解的后置处理器的bean
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
// 注册BeanDefinition到注册表中
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 注册内部管理的用于处理@Autowired,@Value,@Inject以及@Lookup注解的后置处理器bean
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
// 注册内部管理的用于处理JSR-250注解,例如@Resource,@PostConstruct,@PreDestroy的后置处理器bean
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
// 注册内部管理的用于处理JPA注解的后置处理器bean
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 注册内部管理的用于处理@EventListener注解的后置处理器的bean
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
// 注册内部管理用于生产ApplicationListener对象的EventListenerFactory对象
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
04-Spring自定义标签解析的更多相关文章
- Spring自定义标签解析与实现
在Spring Bean注册解析(一)和Spring Bean注册解析(二)中我们讲到,Spring在解析xml文件中的标签的时候会区分当前的标签是四种基本标签(import.alias ...
- Spring 自定义标签配置
前景:经常使用一些依赖于Spring的组件时,发现可以通过自定义配置Spring的标签来实现插件的注入,例如数据库源的配置,Mybatis的配置等.那么这些Spring标签是如何自定义配置的?学习Sp ...
- spring源码深度解析— IOC 之 自定义标签解析
概述 之前我们已经介绍了spring中默认标签的解析,解析来我们将分析自定义标签的解析,我们先回顾下自定义标签解析所使用的方法,如下图所示: 我们看到自定义标签的解析是通过BeanDefinition ...
- 这一次搞懂Spring自定义标签以及注解解析原理
前言 在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如何解析的.同时我们常用的注解如:@Se ...
- 自己构建一个Spring自定义标签以及原理讲解
平时不论是在Spring配置文件中引入其他中间件(比如dubbo),还是使用切面时,都会用到自定义标签.那么配置文件中的自定义标签是如何发挥作用的,或者说程序是如何通过你添加的自定义标签实现相应的功能 ...
- spring基础---->spring自定义标签(一)
Spring具有一个基于架构的扩展机制,可以使用xml文件定义和配置bean.本博客将介绍如何编写自定义XML bean的解析器,并用实例来加以说明.其实我一直相信 等你出现的时候我就知道是你. Sp ...
- spring自定义标签之 自我实现
引言: 最近心情比较难以平静,周末的两天就跑出去散心了,西湖边上走走,看日落,还是不错的.回来博客上发现,在自定义标签上,最后一步实现忘记加上了.其实,人生的路程中,我们总是实现着自我的价值,让自己 ...
- Spring自定义标签的实现
首先 简单写下 spring xml解析的过程 通过一段简单的 调用spring代码开始 public static void main(String[] args) { ApplicationCon ...
- angularjs directive (自定义标签解析)
angularjs directive (自定义标签解析) 定义tpl <!-- 注意要有根标签 --> <div class="list list-inset" ...
- spring自定义标签之 规范定义XSD
引言: spring的配置文件中,一切的标签都是spring定义好的.<bean/>等等,有了定义的规范,才能让用户填写的正常可用.想写自定义标签,但首先需要了解XML Schema De ...
随机推荐
- Shell 元字符 & 变量
Shell 介绍 ## 什么是程序 程序就是一组数据和代码逻辑集合的文件 ## 什么是进程 进程是程序的运行过程,也可以说是操作系统干活的过程,因为是操作系统负责控制硬件来运行应用程序 ps:进程与进 ...
- java-GUI编程学习总结
狂神说java-GUI编程学习总结 1.简介 2.AWT 2.1.实现如图1-2 (1)面向过程写法 (2)内部类写法 (3)完全改造成面向对象 3.Swing 3.1.鼠标花点 3.2.弹窗 3.3 ...
- 牛客网多校第4场 J Hash Function 【思维+并查集建边】
题目链接:戳这里 学习博客:戳这里 题意: 有n个空位,给一个数x,如果x%n位数空的,就把x放上去,如果不是空的,就看(x+1)%n是不是空的. 现在给一个已经放过数的状态,求放数字的顺序.(要求字 ...
- Error Code: 1055.Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'userinfo.
环境:mysql-8.0.15-winx64 问题描述: Error querying database. Cause: java.sql.SQLSyntaxErrorException: Expre ...
- HDU 3949 XOR (线性基第k小)题解
题意: 给出\(n\)个数,求出子集异或第\(k\)小的值,不存在输出-1. 思路: 先用线性基存所有的子集,然后对线性基每一位进行消元,保证只有\(d[i]\)的\(i\)位存在1,那么这样变成了一 ...
- PHP7.1后webshell免杀
严格的D盾 D盾说,我是个严格的人,看到eval我就报木马,"看着像"="就是"木马,宁可错杀一千,绝不放过一个.好了,多说无益,一起看看严格的D盾是如何错杀的 ...
- spring boot集成mybatis只剩两个sql 并提示 Cannot obtain primary key information from the database, generated objects may be incomplete
前言 spring boot集成mybatis时只生成两个sql, 搞了一个早上,终于找到原因了 找了很多办法都没有解决, 最后注意到生成sql的时候打印了一句话: Cannot obtain pri ...
- Chrome V8 系统架构
Chrome V8 系统架构 Chromium 多进程多线程架构 design-documents https://www.chromium.org/developers/design-documen ...
- Angular Routing
Angular Routing v9.0.7 https://angular.io/start/start-routing
- push notifications
push notifications https://developers.google.com/web/fundamentals/push-notifications/ Push API https ...