SpringBoot 自动装配原理分析
SpringBoot 中约定大于配置,这一原理使得配置文件量骤减,但也使得查找起来不太容易。只有了解了其背后的原理,知其然而知其所以然,才不会感到神奇和迷茫。
使用 SpringBoot 版本是: 2.2.6.RELEASE SpringBoot
先来看看 SpringBoot 的主配置类:
@Log4j2
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MingshiApplication.class);
app.addListeners(new ApplicationPidFileWriter());
app.run(args);
}
}
@SpringBootApplication 注解,SpringBoot的这个注解应用在哪个类上就说明这个类是主配置类,SpringBoot就会运行这个类中 main 方法来启动 SpringBoot 项目。那么这个注解到底是什么呢,点进去看看:
@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 是一个组合注解。先点击 @SpringBootConfiguration 这个配置类看看:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
这个注解很简单,表明改类是一个配置类。点击 @Configuration 看看:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@Component 注解说明 Spring的配置类也是 Spring的一个组件,这个也是很简单的注解。
下面我们返回到 @SpringBootApplication 中的 @EnableAutoConfiguration注解,根据字面翻译猜测这个注解是关于自动配置相关的,点击进去看看:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
先看看@AutoConfigurationPackage注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
这个注解是自动配置包,只要使用 @Import 来给 Spring 容器中导入一个组件,这里导入的 Registrar.class。
点击查看 Registrar.class :
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
registerBeanDefinitions() 方法,就是通过这个方法获取扫描的包路径,可以debug看看,在这行代码上打了一个断点:
点击 Evaluate Expresson ,弹出的窗口中点击 Evaluate 按钮,显示获取到的包路径:
那那个metadata是什么呢,在debugger分析器中查看,可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类
说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。
现在包扫描路径获取到了,那具体加载哪些组件呢,看看下面这个注解。
@Import导入的是AutoConfigurationImportSelector 类,(@Import组件就是给Spring容器导入一些组件),点击AutoConfigurationImportSelector 类,看到里面有个方法 getAutoConfigurationEntry(),将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
debug进来查看导入的配置文件:
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件,有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。
那他是如何获取到这些配置类的呢,看看上面这个方法:
... ...
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
... ...
//方法实现
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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 Boot在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。
现在来查看 Spring Boot 中默认的配置文件给我们配置了什么,spring-boot-autoconfigure-2.1.8.RELEASE.jar:
打开 META-INF/spring.factories 文件,可以看到很多的内容。如果在自己的工程中要加扩展的自动配置内容,就要在自己的工程目录结构 META-INF/spring.factories 文件中,按照系统的这个文件例子来配置。
总结:注解 @SpringBootApplication 和 @EnableAutoConfiguration;路径 META-INF/spring.factories,Spring Boot 主方法启动后按照路径进行扫描,所有要注意包的路径。