前言
SpringBoot 采用“约定大于配置”的思想。
SpringBoot 支持自动配置。
约定大于配置
“约定大于配置”(Convention Over Configuration)是一种软件设计范式,其核心思想是:框架为开发者提供一套默认的、符合大多数人习惯的约定(标准、规范),开发者只需要在不遵守这些约定时,才需要显式地编写配置来指定自己的行为。
在 Spring 框架的语境下,尤其是在 Spring Boot 出现后,这一理念得到了极大的体现。
其优势主要体现在以下几个方面:
- 提升开发效率
- 减少决策: 开发者无需在项目初期就纠结于项目结构、配置文件位置、Bean 的命名等琐碎细节。
- 减少样板代码: 框架自动处理了大量常规的配置,开发者只需关注与业务逻辑相关的代码。
- 降低入门和上手门槛
- 新成员加入项目时,如果项目遵循标准的 Spring Boot 约定,他们能更快地理解项目结构并开始工作,因为大部分配置方式都是“众所周知”的。
- 学习和记忆的成本降低,开发者只需要记住“例外情况”该如何配置即可。
- 保持项目的一致性和标准化
- 所有遵循相同约定的项目,其代码结构和配置方式都非常相似。这使得项目更容易维护、交接和自动化(例如,通过标准的 Maven/Gradle 插件进行构建和部署)。
- 减少错误
- 手动编写的配置越少,因配置错误(如拼写错误、遗漏配置项)而导致的问题也就越少。框架提供的默认约定通常是经过充分测试和验证的。
SpringBoot 自动配置
自动配置(Auto-configuration)是 Spring Boot 对“约定大于配置”理念的核心实现机制。
它的工作原理是:Spring Boot 在启动时会扫描项目的 Classpath,根据你引入的依赖 Jar 包,来“猜测”你想要如何配置 Spring 应用,然后自动为你创建和配置所需的 Bean,并注入到 Spring 容器中。
关键组件:
@SpringBootApplication 注解:这是一个组合注解,它包含了一个至关重要的 @EnableAutoConfiguration 注解。spring-boot-autoconfigure Jar 包:这个包里包含了大量针对常见技术(如 JDBC、JPA、Redis、MongoDB 等)的自动配置类。META-INF/spring.factories 文件:在这个文件中,声明了所有在类路径下被发现时需要被处理的自动配置类。- 条件化配置(Conditional):这是自动配置的大脑。Spring Boot 使用大量的
@Conditional 注解(如 @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty)来判断某个自动配置是否应该生效。例如:“如果类路径下有 DataSource 类,并且用户没有自己配置 DataSource Bean,那么我就自动配置一个内存数据库的 DataSource。”
| 方面 | 传统 Spring(配置大于约定) | Spring Boot(约定大于配置) |
|---|
| 配置量 | 大量 XML/Java Config | 极少,甚至为零 |
| 项目搭建 | 复杂,需手动整合各个组件 | 快速,通过 Starter 依赖一键集成 |
| 部署 | 需要外部 Web 服务器 | 内嵌服务器,可执行 Jar/War |
| 学习曲线 | 较陡峭,需了解很多配置细节 | 平缓,开箱即用 |
| 灵活性 | 极高,可完全自定义 | 同样高,通过覆盖自动配置来实现 |
使用指导
通常使用注解声明自动配置。
- 注解
@SpringBootApplication - 注解
@EnableAutoConfiguration
注解 @SpringBootApplication底层使用了注解 @EnableAutoConfiguration,事实上注解 @EnableAutoConfiguration是启动自动配置的唯一注解。
注解 @EnableAutoConfiguration的文档说明如下:
Auto-configuration classes are usually applied based on your classpath and what beans you have defined.
自动配置类通常基于您的类路径以及您定义的 bean 来应用。
Auto-configuration classes are regular Spring @Configuration beans. They are located using ImportCandidates and the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBean annotations).
自动配置类是普通的 Spring @Configuration bean。它们通过 ImportCandidates 和 SpringFactoriesLoader 机制(以此类为键)来定位。通常,自动配置的 bean 是 @Conditional bean(大多数情况下使用 @ConditionalOnClass 和 @ConditionalOnMissingBean 注解)。
类定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // org.springframework.boot.autoconfigure.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 {}
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 忽略自动配置的类
Class<?>[] exclude() default {};
// 忽略自动配置的类的名称
String[] excludeName() default {};
}
|
源码分析
原理概述
注解 @EnableAutoConfiguration通过注解 @Import引入了AutoConfigurationImportSelector.class,该类将加载自动配置类。
待补充完整概述
// 一种ImportSelector的变体,表示在所有@Configuration bean 处理完成之后运行。
// org.springframework.context.annotation.DeferredImportSelector
流程概述
背景知识
在 SpringBoot 中,类 ConfigurationClassPostProcessor用于引导处理 @Configuration 类。自动配置类均为 Spring @Configurationbean,因此由类ConfigurationClassPostProcessor 处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // SpringBoot:
--> `ConfigurationClassPostProcessor` 解析启动类的注解 `@SpringBootApplication`
--> ...
--> 解析注解 `@EnableAutoConfiguration`
--> 解析注解 `@Import`
--> 解析 `AutoConfigurationImportSelector.class`
-> 因该 `selector instanceof DeferredImportSelector`,交由 `DeferredImportSelectorHandler` 处理。
-> 将其存储在缓存 `org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#deferredImportSelectors` 中。
--> 进行其它配置解析...
--> 解析缓存 `org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#deferredImportSelectors` 中的配置,默认仅 1 个配置,即`AutoConfigurationImportSelector.class`。
--> 实际处理逻辑为 `org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports`。
--> 调用`org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports` 和 `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup`中的方法,获取自动配置类的全限定名称字符串。
--> for-each entrys,遍历上一步骤获取的自动配置类进行解析。
--> 调用`org.springframework.util.ClassUtils#forName` 实例化自动配置类。
--> 调用`org.springframework.context.annotation.ConfigurationClassParser#processImports`方法解析这些配置类。
--> 优先作为`ImportSelector`或`ImportBeanDefinitionRegistrar`进行解析。
--> 如果不满足条件,则将其作为普通配置类进行完整解析,调用`org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass`。
|
类定义
上述流程概述涉及的类定义如下
1
2
3
| // org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
|
1
2
3
4
5
6
7
| // 一种`ImportSelector`的变体,表示在所有`@Configuration` bean 处理完成之后运行。
// org.springframework.context.annotation.DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector {}
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {}
|
1
2
3
4
5
6
7
8
9
10
11
| // org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler
private class DeferredImportSelectorHandler {}
// org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping
private static class DeferredImportSelectorGrouping {}
// org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler
private class DeferredImportSelectorGroupingHandler {}
// org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping
private static class DeferredImportSelectorGrouping {}
|
流程详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| private class DeferredImportSelectorHandler {
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
// 因 `deferredImportSelectors` 是默认初始化为空 ArrayList 的,第一次调用该`handle`方法(且未调用过`process()`方法)时,必定不为空,即先缓存下了,延迟批量处理。
// 当调用 `process()`方法后,`deferredImportSelectors` 会被值为空,之后调用`handle`方法时,将不再缓存,而是立即处理。
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
// 缓存。
this.deferredImportSelectors.add(holder);
}
}
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
// 置为 null,之后将不再缓存 selector。
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
|
END