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

相关推荐
玹外之音13 小时前
Spring AI 结构化输出转换器实战:告别字符串解析,拥抱类型安全
spring·openai
爱跑步的程序员~14 小时前
SpringBoot集成SpringAI与Ollama本地大模型
java·后端·spring·ai·llama·springai
百锦再15 小时前
线程安全的单例模式全方位解读:从原理到最佳实践
java·javascript·安全·spring·单例模式·kafka·tomcat
百锦再15 小时前
Java synchronized关键字详解:从入门到原理(两课时)
java·开发语言·struts·spring·kafka·tomcat·maven
百锦再16 小时前
Java重入锁(ReentrantLock)全面解析:从入门到源码深度剖析
java·开发语言·struts·spring·kafka·tomcat·intellij-idea
知识即是力量ol16 小时前
口语八股—— Spring 面试实战指南(终篇):常用注解篇、Spring中的设计模式
java·spring·设计模式·面试·八股·常用注解
无心水17 小时前
6、合纵连横:开源快速开发平台全解析与自建平台架构实战【终篇】
java·后端·科技·spring·面试·架构·开源
JiaHao汤17 小时前
一文掌握 SQL:数据定义、操作与查询完整教程
数据库·sql
l1t18 小时前
利用DeepSeek和qwen 3.5辅助生成SQL优化方法幻灯片视频
数据库·sql·音视频
亓才孓18 小时前
[SpringIOC]NoUniqueBeanDefinitionException
java·spring