SpringBoot--自动配置原理详解
为什么要学习自动配置原理?
原因:在实际开发中,我们经常会定义一些公共的组件,提供各个团队来使用,为了使用方便,我们经常会将公共的组件自定义成starter,如果想自定义starter,必须来了解自动配置原理。
遵循约定大于配置的原则,在springboot程序启动后,起步依赖中的一些bean对象会自动注入到IOC容器中。
这时就需要去springboot中的源码中解析了。
源码分析:
程序中引入spring-boot-starter-web起步依赖后,启动就会自动往IOC容器中注入DispatcherServlet。
代码展示:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
@SpringBootApplicationpublic class SpringbootAutoConfigApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootAutoConfigApplication.class, args);System.out.println(context.getBean(DispatcherServlet.class));}}
分析源码
启动类@SpringBootApplication是一个组合注解,其中组合了三个注解。
第一个注解@springBootConfiguration
作用是声明启动类也是一个配置类:源码如下
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration@Indexedpublic @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
第二个注解(重点):@EnableAutoConfiguration
这是自动配置的核心注解。源码如下:
之前在学习Bean注册时,@Import的第二种用法:@EnableXxxx注解,用于封装@Import注解(导入ImportSelector 接口实现类)。
再看源码。这不就是我们在学习Bean注册使用的方法吗,在进入AutoConfigurationImportSelector类中一探究竟!
public class AutoConfigurationImportSelector implements DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector
这说明AutoConfigurationImportSelector就是ImportSelect接口的实现类。
既然是ImportSelect接口的实现类,那一定重写了selectImports方法。那我们之前在学习注册Bean时,曾说过,selectImports方法会被springboot自动调用从而得到返回的全类名的字符串数组,然后将对应的Bean对象注入到IOC容器中。
但是在源码中springboot并不会调用selectImports方法.
因为此时AutoConfigurationImportSelector实现的是ImportSelector的子类 DeferredImportSelector。而springboot针对于DeferredImportSelector接口的实现类有另外一组处理流程,在这个流程中并不会调用这个selectImports方法。
但是我们还是可以通过解析selectImports方法学习自动配置的原理。
继续回忆:当时在学习注册Bean时,selectImports方法是通过返回一个全类名的数组,从而将Bean对象注入IOC容器中,这些全类名也是从配置文件中读取出来再输入数组中。
再看源码,springboot这里也是需要去读取配置文件中的全类名再返回,而在源码中是调用getAutoConfigurationEntry(annotationMetadata)
新建autoConfigurationEntry
对象调用getConfigurations()方法,可以缩减成
return StringUtils.toStringArray(getAutoConfigurationEntry(annotationMetadata).getConfigurations());
因此重点就是getAutoConfigurationEntry()方法,这里面应该可以得知配置文件的位置。
继续跟进,去看getAutoConfigurationEntry()方法的源码,可以看到返回了一个AutoConfigurationEntry对象,里面有两个参数,其中configurations应该和我们的配置文件有关,
继续跟进,发现通过调用getCandidateConfigurations方法生成字符串数组。
继续跟进,去查看getCandidateConfigurations方法源码:
至此,我们终于找到了配置文件的所在地,同时也绕不开一个类:autoConfigurationAnnotation类,这个类决定了配置文件,所以需要继续跟进。
同时我们在解析springboot中的starter的起步依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>
在跟进解析
我们在去导入的依赖库中寻找
进入到这些imports文件中,里面果然是一堆类的全类名。而且都是自动配置类。
我们也可以去看看这些自动配置类如何书写的,我们可以去找DispatcherServletAutoConfiguration
找到之后,发现DispatcherServletAutoConfiguration类上声明了一个@AutoConfiguration注解,(自动配置)
我们去看下@AutoConfiguration注解的源码
同时我们在DispatcherServletAutoConfiguration类上也发现了一个老熟人,在我们学习注册条件时,学到的Conditional注解的衍生注解@ConditionalOnClass: 当前环境存在指定的类时,该注解声明下的Bean才会被注册。
在这里的意思就是@ConditionalOnClass(DispatcherServlet.class)
:如果说运行环境中有DispatcherServlet类,这是DispatcherServletAutoConfiguration类(DispatcherServlet自动配置类)就生效!就可以自动注入一个DispatcherServlet Bean对象。
继续分析源码:
我们在DispatcherServletAutoConfiguration中还发现了一个内部配置类DispatcherServletConfiguration。
而且这个内部配置类里面的方法就是我们学习Bean注册时所用的注入方法,用@Bean注解声明,返回DispatcherServlet对象。
而且这里面最核心的地方在于,
将DispatcherServletAutoConfiguration写进指定的配置文件中。
这时,springboot就可以自动的到配置文件中读取,将自动配置类的对象注入到IOC容器中。
又由于自动配置类的内部还有一个配置类,且内部配置类中还有用@Bean声明的方法,
因此,springboot还是会继续解析,直至解析完毕,将所有的Bean对象注入到IOC容器中。
总结:自动配置的核心就是"META-INF/spring/"+ this.autoConfigurationAnnotation.getName() + ".imports配置文件。
在springboot2.7版本之前,自动配置使用的这个配置文件是spring.factories,springboot会从这个配置文件中读取自动配置类的全类名。
在springboot2.7到3.0之间,springboot同时兼容了.imports文件以及spring.factories,但是在3.0以后就只支持.imports文件了。
至此。自动配置的原理已经明了。
再回到最开始,我们在学习Bean注册时的代码是自动配置吗?
明显没有,因为我们还需要去手动的提供配置类,还需要去写Imports注解。
如果要达到自动配置的效果,配置类不应该是自己提供。
应该是谁提供Bean对象,谁就应该提供这些配置类,因此配置类应该由jar包提供。
接下来我们还要jar包提供一个自动配置类,并且在自动配置类上声明两个注解:@AutoConfiguration(声明自动配置类),还有@Import()(导入配置类。
最后我们需要提供一个.imports配置文件,将自动配置类的全类名放在.imports配置文件中。这样我们就达到了自动配置的效果.
案例展示
自定义maven项目,结合Bean注册以及注册条件中的知识创建自动配置项目并打包成jar包:common-pojo-2.0
首先新建Maven项目,新建src目录及resources目录,在如下建立包
代码如下:
pom.xml:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.4.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.38</version></dependency></dependencies>
pojo包下
Country
@Data@AllArgsConstructor@NoArgsConstructorpublic class Country {private String name;private String code;}
Province
@Data@AllArgsConstructor@NoArgsConstructorpublic class Province {private String name;private String direction;}
config包下
commonConfig
public class CommonConfig {@ConditionalOnProperty(prefix = "country",value = {"name","code"})@Bean//注入country对象public Country country(@Value("${country.name}") String name, @Value("${country.code}") String code){return new Country(name, code);}// @Bean("aa")//如果方法内部需要使用IOC容器中的bean,则需要传入参数,springboot会自动传入//如果方法内部没有使用IOC容器中的bean,则不需要传入参数@Beanpublic Province province(){return new Province();}}
commonAutoConfig
@AutoConfiguration@Import({CommonConfig.class})public class CommonAutoConfig {public CommonAutoConfig() {}}
META-INF包下 org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.lyc.config.CommonAutoConfig
代码如上所示:
最终使用maven工具打包,从target包下拿到jar包
将jar包与该项目放于同一文件夹下。
在命令行中输入以下指令
mvn install:install-file -Dfile="E:\桌面\测试\common-pojo-2.0-1.0-SNAPSHOT.jar" -DgroupId=com.lyc -DartifactId=common-pojo-2.0 -Dversion=1.0-SNAPSHOT -Dpackaging=jar
最终在pom文件中输入依赖
<dependency><groupId>com.lyc</groupId><artifactId>common-pojo-2.0</artifactId><version>1.0-SNAPSHOT</version></dependency>
最终得到:
进行测试,看Province Bean对象是否注入IOC容器中
在配置文件中给属性赋值
country:name: 中国code: CN
public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootAutoConfigApplication.class, args);System.out.println(context.getBean(DispatcherServlet.class));System.out.println(context.getBean("province"));System.out.println(context.getBean("country"));}
结果:
测试成功
面试高频:说一说springBoot自动配置原理?
首先在主启动类上添加了@SpringBootApplication注解,这是一个组合注解,其中包含了@EnableAutoConfiguration注解。这个注解就是开启自动配置的关键注解。
在@EnableAutoConfiguration注解中,又组合了@Import注解,导入了AutoConfigurationImportSelector类,这类实现了selectImports方法,这个方法经过层层调用,最终会读取META-INF目录下的后缀名为imports的文件。
在springboot2.7版本以前,读取的是spring.factories文件,在2.7到3.0之间兼容,3.0版本之后只允许读取imports文件。
这个imports文件中包含了许多自动配置类的全类名,springboot底层会自动读取imports文件中的全类名。
解析注册条件,也就是@Conditional及其衍生注解,把满足注册条件的Bean对象自动注入到IOC容器中。
希望对大家有所帮助!