一文足够,SpringBoot自动装配底层源码

目录

自动装配原理

开始深入源码

总结自动装配原理


首先明白一个概念,什么是自动装配?

我们在项目中建一个yaml或者properties文件,里面配置一些参数,如redis,在pom中引入启动器,之后就能用redis,自动把这些集成到spring中,这就是自动装配。

先来提前剧透:

加载spring.factories文件

标记好启动类

解析启动类的注解

加载EnableAutoConfiguration

 

自动装配原理

开始深入源码

启动类中

创建SpringApplication,调用run方法,这里面是核心逻辑

为什么把启动类作为参数传进去? 因为后期需要获取这个启动类上的注解,进入run方法

primarySource就是启动类

这里new SpringApplication,注意看源码时不要忽略new,为什么?因为new,会调用构造方法,而很多初始化操作会在构造方法中定义

 

进入

注意下面的两行代码

这里涉及到spring.factories这个文件是如何读取与加载的,包括用户自己怎么定义spring.factories

我们先进入setInitializers这行代码中

先获取工厂的名字,之后加载工厂

从缓存中获取,如果缓存中没有会从classpath中查找我们需要的配置文件,配置属性

指定了路径

方法的后半部分,就是把获取的属性,变成properties的格式

最后一步就是把查询到的数据,放到缓存中,方便下次数据的读取。

不需要重新查询,不需要重新加载配置文件等,加快速度

 

 之后返回结果到刚才的方法,把获取的类名称,实例化

 返回到

 setListeners这行代码的处理逻辑和上面一样

public ConfigurableApplicationContext run(String... args) {
//时间的处理
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
//上下文  就是一个全局变量,保存全局数据,供其他人使用
		ConfigurableApplicationContext context = null;
//异常汇报器
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
//获取监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
//  spring,springBoot对于观察者模式,使用是非常多的
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
//封装环境变量
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
//上面都是一些赋值准备工作
//下面这行才是重要
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
		......
	}

spring,springBoot对于观察者模式使用是非常多的,如Listener,event

其中Environment接口有个实现类,standardEnvironment

这个standerEnvironment类会加载系统的环境和属性值,比如java版本,java classpath,编码格式等

进入到prepareContext方法中 

前面讲过这个primarySource就是启动类,这里会去找primarySource,也就是找启动类

那么为什么要找启动类?是为了解析启动类上的注解,这是核心

之后load加载,进入方法

提示:Spring通过反射创建对象前,必须要有BeanDefinition(bean定义),BeanDefinition中保存的是属性值

 创建BeanDefinition,设置一些属性值,之后调用loader.load方法

 判断source类型,我们的是类启动器,所以是Class,满足第一个if判断

 

通过注解读取器来读取注解信息 ,注意这里只是加载好注解信息,做好标识,没有解析。之后返回到前面的prepareContext中,此时已经加载完了注解信息

返回到prepareContext中,已加载注解信息,待解析。

refresh在spring中是个很重要的方法,当带有refresh前缀时,表示它要调用refresh方法了,同时这个方法也是重要的。

如果点击进入refreshContext方法,其底层就是调用的refresh,如果不调用,是没法创建bean的

在详细分析refreshContext方法前,我们先来了解一些知识点

spring为什么这么火?

一是因为全生态,有所有的技术组件,我想用的技术,几乎都有相应的框架。

二是扩展性,代码不能是死的,可以根据你的业务需求来动态扩展

spring中有两个重要的扩展接口:postProcessor(后置处理器,个人认为增强器),是为了完成扩展功能的,做对应的扩展实现。有两个代表:BeanFactoryPostProcessor和BeanPostProcessor

 SpringBoot自动装配的实质就是从spring.factories文件中获取到相应的Bean对象,并由Spring容器来帮我们进行管理

spring.factories文件中存的是类的完全限定名,我们可以通过反射来实例化出具体的Bean对象,而这些Bean对象会放到BeanFactory中。如果我想扩展,就用BeanFactoryPostProcessor

注意逻辑

无论是配置,还是注解,都是为了创建BeanDefinition,之后通过反射实例化获取Bean对象

refreshContext方法最后还是调用的refresh方法

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备刷新,初始化容器等
			prepareRefresh();

			// 获取最新的BeanFactory工厂,用来生产bean
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 设置属性
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// 很重要  增强器或者后置处理器  这里实现的自动装配
				invokeBeanFactoryPostProcessors(beanFactory);

				......
	}

进入invokeBeanFactoryPostProcessors(beanFactory)方法

里面有

这个BeanDefinitionRegistryPostProcessor就是刚才BeanFactoryPostProcessor的子接口

这个方法就是去Bean工厂中,找有没有归属于这个BeanDefinitionRegistryPostProcessor的类对象,最后返回

 这里会有个转换,在AnnotationConfigUtils中 定义了这个字符串

然后根据字符串匹配对应的类 

比如上面的字符串就匹配后就会BeanDefinition中放入ConfigirationClassPostProcessor,生成BeanDefinition

实际解析的类是ConfigirationClassPostProcessor,本质是BeanFactoryPostProcessor,不是配置类,是配置的增强类

继续往下

这个方法内部会进行一些调用

通过BeanDefinitionRegistry的BeanDefinitionName方法获取到集合

之后循环判断,根据规则判断集合中的每一个BeanName是不是配置增强类,

经过判断,最后只获得启动类。

(如下图Debug,我们的启动类就叫springbootDataApplication)

之后就开始解析

解析每一个被@Configuration修饰的类,我们的启动类也被修饰了

用解析器,解析启动类

里面通过递归来逐层解析@PropertySource,@ComponentScan,@Import,@Bean等注解

解析完成后到下面这个方法,这个方法比较重要

各种方法调用 

 

   

进入getCandidateConfigurations方法中 

注意这里的SpringFactoryLoader,就是前面setInitializers那部分,读取spring.factory,然后

前面已经读取了spring.factory放到缓存中了。

根据类型把EnableAutoConfiguration取出来

 注意取出来多少个EnableAutoConfiguration?一百多个

但是不会全部加载,pom里依赖什么,我加入什么

所以在getExclusions中判断去除哪些东西,remove它们。

最后configurations里就是我们需要用到的配置

 之后获取实例

总结自动装配原理


1、当启动springboot应用程序的时候,会先创建SpringApplication的对象,在对象的构造方法中会进行某些参数的初始化工作,最主要的是判断当前应用程序的类型以及初始化器和监听器,在这个过程中会加载整个应用程序中的spring.factories文件,将文件的内容放到缓存对象中,方便后续获取。


2、SpringApplication对象创建完成之后,开始执行run方法,来完成整个启动,启动过程中最主要的有两个方法,第一个叫做prepareContext,第二个叫做refreshContext,在这两个关键步骤中完整了自动装配的核心功能,前面的处理逻辑包含了上下文对象的创建,banner的打印,异常报告期的准备等各个准备工作,方便后续来进行调用。


3、在prepareContext方法中主要完成的是对上下文对象的初始化操作,包括了属性值的设置,比如环境对象,在整个过程中有一个非常重要的方法,叫做load,load主要完成一件事,将当前启动类做为一个beanDefinition注册到registry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplicaiton,@EnableAutoConfiguration等注解的解析工作。


4、在refreshContext方法中会进行整个容器刷新过程,会调用中spring中的refresh方法,refresh中有13个非常关键的方法,来完成整个spring应用程序的启动,在自动装配过程中,会调用invokeBeanFactorvPostProcessor方法,在此方法中主要是对ConfigurationClassPostProcessor类的处理,这次是BeanFactoryPostProcessor的子类也是BeanDefinitionRegistryPostProcessor的子类,在调用的时候会先调用BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,然后调用postProcessBeanFactory方法,在执行postProcesskeanDefinitionRegistry的时候回解析处理各种注解,包含@PropertySource,@ComponentScan,@ComponentScans,@Bean,@lmport等注解,最主要的是@Import注解的解析


5、在解析@lmport注解的时候,会有一个getlmports的方法,从主类开始递归解析注解,把所有包含@lmport的注解都解析到,然后在processlmport方法中对lmport的类进行分类,此处主要识别的时候AutoConfigurationlmportSelect归属于ImportSelect的子类,在后续过程中会调用deferredlmportSelectorHandler中的process方法,来完整EnableAutoConfiguration的加载

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老李头喽

高级内容,进一步深入JA领域

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值