Springboot的自动配置原理
Springboot的自动配置原理大致分为两个步骤:
- 使用类似 Java SPI 机制获取自动配置类
- 根据自动配置类生成对应的 bean
其中,第二步根据配置类生成 bean ,只是我们常见的配置,可能会多一些条件配置注解,所以不做重点内容。这里重要阐述第一步。具体如下:
1. Java SPI 机制
java SPI 是 Java 内置的服务发现机制。这种发现机制帮助我们发现并装配服务。这个自动发现并装配的过程使得我们仅通过更换依赖,即可更换服务提供者,而无需修改代码。下面是例子:
首先构建如下4个项目:
├─Client
├─Interface
├─Provider1
└─Provider2
其中 Client 是客户端,Interface 是实现和客户端共用的接口,Provider1/Provider2 是两种接口实现。具体内容如下:
-
共用接口如下
public interface MyInterface { public void sayHi(String message); }
-
实现 Provider1 如下
public class MyInterfaceImpl1 implements MyInterface { @Override public void sayHi(String mesg){ System.out.println("Hello, I am " + mesg + " --by implement1"); } }
-
在 Provider1 项目下 resources/META-INF/services 下新增文件 org.example.MyInterface,文件内容如下:(文件名即共用接口名称)
org.example.MyInterfaceImpl1
-
同样类似实现 Provider2
-
在 Client 项目的 pom.xml 中引入项目 Provider1,并在使用如下实现
public class App { public static void main( String[] args ) { ServiceLoader<MyInterface> mI = ServiceLoader.load(MyInterface.class); for(MyInterface tmp : mI){ tmp.sayHi("MAIN"); } } }
-
输出结果
Hello, I am MAIN --by implement1 // 若将 Client 的 pom.xml 中依赖改为 Provider2 则会输出如下 Hello, I am MAIN --by implement2
-
这样既可让 Client 根据依赖获取实现,而无需显示引用依赖(自动发现并装配)
Springboot 的自动配置类使用类似的技术,它在每个 starter 包的每个 META-INF 目录下放一个 spring.factories 文件,文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xx.xx.xx.XXConfiguration1,\
xx.xx.xx.XXConfiguration2
这个配置的效果等效于给容器 Import 这些配置类。这样容器就可以自动的对某某框架配置 bean 了。
2. Springboot 获取配置类过程
Springboot 的自动配置过程如下:
-
应用启动类
@SpringBootApplication public class SpringBootAutoconfigImportAnnotationApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAutoconfigImportAnnotationApplication.class, args); System.out.println("Yes, It's running"); } }
在
SpringApplication.run()
这一行,Springboot 应用会启动,它启动过程中会有一个 BeanPostProcessor 来处理配置中的注解,以此从配置中生成 BeanDefinition。这个 BeanPostProcessor 处理
@SpringBootApplication
从而触发配置加载。@SpringBootApplication
的定义如下 -
@SpringBootApplication
注解定义@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 {
这里面跟自动配置相关关的就是
@EnableAutoConfiguration
它的定义如下 -
@EnableAutoConfiguration
的定义如下@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
这其中
@Import(AutoConfigurationImportSelector.class)
即用来导入类路径中的自动配置类。 -
当处理到
AutoConfigurationImportSelector
时,它的处理流程如下:// 1. 获取自动配置,然后保存 public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 1.1 这里调用它的 getAutoConfigurationEntry 方法获取自动配置 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(annotationMetadata); // 1.2 这里保存 this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } // 2. 获取自动配置 - 读取 spring.factories 并去重,过滤,等操作 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // 2.1 这里从前面第一节的 spring.factories 中获取配置类 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } // 3. 从 spring.factories 中获取配置类 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 3.1 就是这里,使用 SpringFactoriesLoader 来获取配置类信息 List<String> configurations = new ArrayList<>( SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())); ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
经过这么一套流程下面,Springboot 就可以获取类路径中的配置类,就可以根据依赖给对应框架生成bean,然后就实现了 Springboot 的自动配置。