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的解析流程,下个章节继续讲解注解形式的解析

相关推荐
Wx-bishekaifayuan3 小时前
springboot生活商城系统21035-计算机课程设计、毕业设计
spring boot·后端·python·spring·elasticsearch·golang·课程设计
卓怡学长3 小时前
w206基于SpringBoot的社区团购小程序设计与实现
java·spring boot·spring·intellij-idea
知守观8 小时前
@Transactional 事务失效排查,try-catch 吞异常导致回滚失败(附源码分析)
后端·spring
码农-0049 小时前
Springboot 集成 Ehcache操作数据库显示SQL语句设置
数据库·spring boot·sql
钱栈up9 小时前
全量 21 个失败、单跑全绿:泄漏进连接池的 SQL 变量
sql·单元测试·测试
旺仔不是程序员9 小时前
pg_trgm GIN 索引:PostgreSQL 正则、模糊与近似度查询的三合一加速器
数据库·后端·sql
考虑考虑9 小时前
SQL中的 CASE WHEN
数据库·后端·sql
旺仔不是程序员9 小时前
索引膨胀与重建:PostgreSQL REINDEX 生产实践
数据库·后端·sql
漂着的圆木6 天前
Agent 功能参与度:Copilot 怎么算
sql·数据分析·agent·githubcopilot·度量
Bingo_BIG6 天前
Java Spring框架:单表的查询、新增、修改、删除、导入、导出
java·spring·前后端