1. SpringBoot自动装配原理
接触过SpringBoot的同学肯定都知道在启动类上有一个@SpringBootApplication注解,他就是自动装配的奥秘所在。
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 1.2.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 省略代码
}
点开@SpringBootApplication注解,先看它的注释,大意是:
表示一个声明了一个或多个由@Bean注解的方法的配置类,并触发了自动配置和组件扫描。可以看到,它由多个注解组合而成,最主要的就是三个注解:
它是一个等价于 @Configuration、 @EnableAutoConfiguration和 @ComponentScan的更方便的注解。
(1) @SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
// 省略代码
}
@SpringBootConfiguration其实就相当于@Configuration,就不做过多解释了。
(2) @EnableAutoConfiguration
这个注解是自动装配的核心注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 省略代码
}
它由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组成:
1) @AutoConfigurationPackage
/**
* Registers packages with {@link AutoConfigurationPackages}. When no {@link #basePackages
* base packages} or {@link #basePackageClasses base package classes} are specified, the
* package of the annotated class is registered.
*
* @author Phillip Webb
* @since 1.3.0
* @see AutoConfigurationPackages
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
// 省略代码
}
可以看下它的注释,大意是:
使用 @AutoConfigurationPackage注解来注册包。它使用了@Import注解导入了AutoConfigurationPackages.Registrar这个内部类,它的作用是把扫描路径注册到容器的全局变量中,这样就可以提供给一些JPA框架用来查询到扫描路径。
当没有指定基础包或者基础包类时,被它所注解的类就会被注册。
2) @Import(AutoConfigurationImportSelector.class) 这里使用@Import导入了AutoConfigurationImportSelector类,这个才是最核心的注解。
在AutoConfigurationImportSelector中有一个selectImports方法,它会返回所有需要加载到容器中的类的全路径,SpringBoot在获取到这些类的全路径的时候,会使用反射的方式将它们注入到容器中。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
那selectImports是怎么做到返回这些类的全路径的呢?
重点在于getAutoConfigurationEntry方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
可以看到在getAutoConfigurationEntry方法中,最重要的就是使用getCandidateConfigurations方法获取所有候选的配置类:
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
可以看到,原来是使用了Spring的SPI机制来获取要加载的类的全路径的!
继续跟代码可以看到,在SpringFactorieLoader类中,通过类加载器去加载META-INF/spring.factories文件中的配置,随便点开一个SpringBoot的jar,比如spring-boot-autoconfigure,可以看到果然有一个META-INF/spring.factories文件。
这个文件中配置了一系列key-value的值,这里我们只关注EnableAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
// 省略配置
看到这里应该大概就明白了SpringBoot自动装配的原理了,其实就是通过读取各个jar包中的META-INF/spring.factories中的配置项,来获取类的全路径,然后通过@Configuration和@Bean注入到容器中。
(3) @ComponentScan
【SpringBoot自动装配原理】这个注解应该很熟悉了,是用来设置扫描路径的,默认情况下,它会将当前包以及其子包中的Bean注入到容器中。
推荐阅读
- 第五节:SpringBoot常用注解介绍
- 第四节:SpringBoot中web模版数据渲染展示
- SpringBoot2022【草稿】
- 聊聊springboot项目全局异常处理那些事儿
- 第一节:创建SpringBoot项目并运行HelloWorld
- springboot管理系统[基于员工角色和文件权限的分级的后台管理系统源码]
- SpringBoot之@ComponentScan和@SpringBootApplication扫描覆盖问题
- mybatis|记mybatis查询null字段导致的NPE
- SpringBoot|SpringBoot 整合 druid数据源
- springboot项目配置application添加图片映射 (windows and linux 都可使用)