springboot自动装配几乎是现在面试必问的面试题,要是逐行分析自动装配流程肯定是很复杂的,因此我们从大体上来梳理即可。
一、 自动装配总览
首先要搞清楚两个问题,springboot自动装配是什么?解决了什么问题?
springboot自动装配是一种基于约定和条件判断的配置机制,通过扫描项目的类路径(Classpath)和依赖关系,自动识别并加载所需的spring组件(如Bean、数据源、Web配置等)。开发者无需手动编写大量XML或java配置代码,即可实现功能的快速集成。
解决的问题如下:
(1)传统spring配置复杂
问题:传统spring项目需手动配置大量Bean(如数据源、事务管理器等),依赖XML或javaConfig,配置繁琐且易出错。
解决:自动装配通过预定义的starter依赖和条件化配置,自动完成基础组件的注册和初始化,减少重复配置。
(2)依赖管理混乱
问题:以前对第三方库集成时需手动处理依赖版本冲突和兼容性问题。
解决:starter机制封装了技术栈的依赖包(如MyBatis、Redis),统一版本管理,开发者只需声明starter即可引入所需功能。
(3)开发效率低
问题:以前开发者需要花费大量时间处理配置,而非业务逻辑。
解决:自动装配按需加载组件,通过条件注解(如@ConditionalOnClass)确保仅加载实际需要的配置,避免资源浪费。
二、自动装配实现的原理
2.1 常规理解
网上大部分帖子讲到自动装配基本上上来就是说在启动类的上面有个@SpringBootApplication注解,这个注解是个复合注解。
复合注解中有一个@EnableAutoConfiguration注解,如下图所示
其实@EnableAutoConfiguration也是一个复合注解,如下图所示,这里面有一个很重要的注解@Import,这个注解是要将外部配置或组件导入到spring ioc容器之中。也就是要将AutoConfigurationImportSelector.class导入到容器之中。
那么我们接着分析AutoConfigurationImportSelector这个类,这个类实现了DeferredImportSelector接口,如下图所示
而DeferredImportSelector继承了ImportSelector类,如下图所示
在ImportSelector接口类中有一个selectImports接口。如下图所示
因此AutoConfigurationImportSelector实现了selectImports接口,我们看看具体的实现,这里面调用了this.getAutoConfigurationEntry方法,如下图所示。
this.getAutoConfigurationEntry这个方法内部有一行代码List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);如下图所示
在getCandidateConfigurations方法内部调用了SpringFactoriesLoader.loadFactoryNames方法,如下图所示
在loadFactoryNames这个方法内部调用了loadSpringFactories方法,在这个方法里面有加载META-INF/spring.factories文件中的类的代码,从而自动将spring.factories中的类加载到容器中,这是主流的观点。
2.2 深入理解
从上面常规理解来看,好像@SpringBootApplication这个注解自己就完成了自动装配的整个流程,其实真实的自动装配远比这个要复杂的多。
2.2.1 启动springboot应用程序
当启动springboot应用程序的时候,首先会创建SpringApplication的对象,在对象的构造方法中会进行某些参数的初始化工作,最主要的是判断当前应用程序的类型以及初始化器和监听器,在这个过程中会加载整个应用程序中的spring.factories文件,将文件的内容放到缓存对象中,方便后续获取。
为方便大家理解,我截几张图来展示
进入到run方法中,如下所示
我们继续进入到run方法内部,如下图所示,我们往往会忽略下面方法中new SpringApplication里面的逻辑,殊不知,spring.factories文件第一次被加载就发生在这个里面。
我们进入new SpringApplication内部,如下所示
进入内部构造方法,如下图所示,可以看到有调用getSpringFactoriesInstances这个方法。
进入到getSpringFactoriesInstances方法内部如下图所示
继续进入到方法内部,可以看到调用了SpringFactoriesLoader.loadFactoryNames
而这个内部就可以看到去加载了META-INF/spring.factories文件中的配置类到缓存cache中,这样后面就不用再重复读取spring.factories中的内容了,直接从缓存中获取即可。
从这里可以看出,我们读取spring.factories文件中的配置类的时候,@SpringBootApplication还没发挥作用呢。因此并不是靠@SpringBootApplication去读取的spring.factories文件。这个注解的内部只是从缓存中读取内容而已。
2.2.2 执行SpringApplication对象的run方法
SpringApplication对象创建完成之后,开始执行run方法,来完成整个启动,启动过程中最主要的有两个方法,第一个叫做prepareContext,第二个叫做refreshContext,在这两个关键步骤中完整了自动装配的核心功能,前面的处理逻辑包含了上下文对象的创建,banner的打印,异常报告期的准备等各个准备工作,方便后续来进行调用。
2.2.3 prepareContext方法概要
在prepareContext方法中主要完成的是对上下文对象的初始化操作,包括了属性值的设置,比如环境对象,在整个过程中有一个非常重要的方法,叫做load,load主要完成一件事,将当前启动类做为一个beanDefinition注册到registry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplication,@EnableAutoConfiguration等注解的解析工作。
2.2.4 refreshContext方法概要
在refreshContext方法中会进行整个容器刷新过程,会调用spring中的refresh方法,refresh中有13个非常关键的方法,来完成整个spring应用程序的启动,在自动装配过程中,会调用invokeBeanFactoryPostProcessor方法,在此方法中主要是对ConfigurationClassPostProcessor类的处理,这个是BeanFactoryPostProcessor的子类,也是BeanDefinitionRegistryPostProcessor的实现类,在调用的时候会先调用BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,然后调用postProcessBeanFactory方法,在执行postProcessBeanDefinitionRegistry的时候会解析处理各种注解,包含@PropertySource,@ComponentScan,@ComponentScans,@Bean,@Import等注解,最主要的是@Import注解的解析。
2.2.5 @Import注解概要
在解析@Import注解的时候,会有一个getImports的方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,然后在processImport方法中对Import的类进行分类,此处主要识别到AutoConfigurationImportSelect归属于ImportSelect的子类,在后续过程中会调用deferredImportSelectHandler中的process方法,来完整EnableAutoConfiguration的加载。
以上就是我对springboot自动装配的简单理解。