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

相关推荐
qq_三哥啊1 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis
别惹CC2 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj2 小时前
Spring事物
java·spring
javachen__4 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
IT毕设实战小研10 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋11 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国12 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
武昌库里写JAVA14 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Java小白程序员15 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
老虎062715 小时前
数据库基础—SQL语句总结及在开发时
数据库·sql·oracle