spring中xml的解析与beanDefinition封装(1)

ClassPathXmlApplicationContext ctx1 = new ClassPathXmlApplicationContext("spring.xml");

今天看下最原始spring解析spring.xml的流程,虽然现在已经不再使用xml去配置bean,而是使用注解类似@Component,@Service等去处理,但是注解的处理与xml解析有很多相似之处,对xml解析流程清楚之后,那么对注解的解析也就很清楚了,所以今天从最原始的ClassPathXmlApplicationContext流程开始看起。

首先会进入到refresh()方法的obtainFreshBeanFactory方法中,在这个方法中可以看到loadBeanDefinitions方法,这里会将spring.xml中配置的所有标签进行解析成beanDefinition。

创建xmlBeanDefinitionReader对象,并通过reader对象加载配置文件

根据加载的配置文件把配置文件封装成document 对象

创建BeanDefinitionDocumentReader 对象,DocumentReader 负责对document 对象解析

parseDefaultElement(ele, delegate);负责常规标签解析

delegate.parseCustomElement(ele);负责自定义标签解析

最终解析的标签封装成BeanDefinition 并缓存到容器中

先看parseDefaultElement方法,这里会去解析<import>,<bean>,<alias>,<beans>标签,我们点进处理<bean>标签的逻辑

可以看到这里主要是做了两件事情,先是将bean标签解析为beanDefinition,然后将beanDefinition注册到BeanDefinitionRegistry中,后面实例化要用到BeanDefinitionRegistry中的beanDefinition

1、创建GenericBeanDefinition对象

2、解析bean标签的属性,并把解析出来的属性设置到BeanDefinition中

3、解析bean中的meta标签

4、解析中的lookup-method标签

5、解析bean中的replaced-method标签

6、解析bean中的construstor-arg

7、解析bean中的property标签

然后再看下parseCustomElement方法

1、获取自定义标签的namespace 命令空间,例如:

http\://www.springframework.org/schema/context

String namespaceUri = getNamespaceURI(ele);

2、根据命令空间获取NamespaceHandler 对象。

NamespaceUri 和NamespaceHandler 之间会建立一个映射,spring 会从所有的spring

jar 包中扫描spring.handlers 文件,建立映射关系。

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

反射获取NamespaceHandler 实例,并调用init()方法

init()方法中会调用registerBeanDefinitionParser方法,可以看到解析component-scan的是ComponentScanBeanDefinitionParser方法

然后再回到最外面的方法,

然后进入到ComponentScanBeanDefinitionParser的parse方法可以看到,这里先是取出<componet-scan>标签的包路径,然后新创建一个ClassPathBeanDefinitionScanner,执行doScan(basePackages)方法去将bean解析为beanDefinition,然后注册到BeanDefinitionRegistry中

我们继续往doScan方法里面看,可以看到findCadidateComponets方法就是扫描到的所有的包路径下的beanDefinition,最终调用registerBeanDefinition注入到容器中

findCandidateComponets处理逻辑如下

  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有.class 文件(Spring源码中将

此文件包装成了Resource对象)

  1. 遍历每个Resource对象

  2. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中

MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,

MetadataReader的具体实现类为SimpleMetadataReader)

  1. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选

(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定

的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)

  1. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition

  2. 再基于metadataReader判断是不是对应的类是不是接口或抽象类

  3. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集

MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有

  1. 获取类的名字、

  2. 获取父类的名字

  3. 获取所实现的所有接口名

  4. 获取所有内部类的名字

  5. 判断是不是抽象类

  6. 判断是不是接口

  7. 判断是不是一个注解

  8. 获取拥有某个注解的方法集合

  9. 获取类上添加的所有注解信息

  10. 获取类上添加的所有注解类型集合

值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是

利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对

象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,

它即可以存储类的名字,也可以存储class对象)

最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或

解析spring.xml文件的<bean/>,或者@Bean注解得到BeanDefinition对象。

以上就是<componet-scan>的处理逻辑。

每一个标签会对应不同的handler,具体要看哪个标签的解析,只需要看对应的handler即可

以上就是spring的xml的解析流程,下个章节继续讲解注解形式的解析

相关推荐
大云计算机毕设1 小时前
【2026计算机毕设选题】计算机毕设全新推荐项目选题指南(70+精选热门方向)
spring·数据分析·毕业设计·课程设计·毕设
空空kkk1 小时前
SpringMVC——拦截器
java·数据库·spring·拦截器
梵得儿SHI2 小时前
(第七篇)Spring AI 基础入门总结:四层技术栈全景图 + 三大坑根治方案 + RAG 进阶预告
java·人工智能·spring·springai的四大核心能力·向量维度·prompt模板化·向量存储检索
q***42823 小时前
SpringCloud-持久层框架MyBatis Plus的使用与原理详解
spring·spring cloud·mybatis
KotlinKUG贵州3 小时前
SpringGateway-MVC对SSE转发出现阻塞响应问题的分析和解决
spring·spring cloud·kotlin
l***77523 小时前
总结:Spring Boot 之spring.factories
java·spring boot·spring
2***s6724 小时前
给SQL server数据库表字段添加注释SQL,附修改、删除注释SQL及演示
数据库·sql·oracle
x***01064 小时前
springboot中配置logback-spring.xml
spring boot·spring·logback
AI2中文网5 小时前
AppInventor2 使用 SQLite(三)带条件过滤查询表数据
数据库·sql·sqlite·select·app inventor 2·appinventor·tableview
q***2516 小时前
Spring容器的开启与关闭
java·后端·spring