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

相关推荐
cmdch20172 小时前
Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题
数据库·sql·mybatis
程序猿小柒3 小时前
【Spark】Spark SQL执行计划-精简版
大数据·sql·spark
撒呼呼3 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
天使day4 小时前
SpringMVC
java·spring·java-ee
听见~4 小时前
SQL优化
数据库·sql
壹佰大多5 小时前
【spring-cloud-gateway总结】
java·spring·gateway
CodeChampion5 小时前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
秋意钟5 小时前
Spring框架处理时间类型格式
java·后端·spring
明矾java6 小时前
Mysql-SQL执行流程解析
数据库·sql·mysql
NY69 小时前
mysql运维篇笔记——日志,主从复制,分库分表,读写分离
数据库·sql