自动配置原理
Spring Boot 的核心特性之一就是自动配置(Auto-configuration)。本章将深入剖析自动配置的工作原理,帮助你理解 Spring Boot "约定优于配置" 的设计理念。
什么是自动配置?
自动配置是 Spring Boot 根据 classpath 中的依赖自动配置 Spring 应用的机制。它通过条件判断决定哪些配置应该生效,从而减少开发者的手动配置工作。
传统 Spring vs Spring Boot
传统 Spring 配置:
<!-- web.xml -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
</servlet>
<!-- spring-mvc.xml -->
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
Spring Boot 自动配置:
只需添加依赖,无需任何配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 会自动配置:
- DispatcherServlet
- ViewResolver
- MessageConverter
- 内嵌 Tomcat
自动配置原理
核心注解:@EnableAutoConfiguration
@EnableAutoConfiguration 是自动配置的核心注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
关键组件:
- @AutoConfigurationPackage:将主配置类所在包作为自动配置包
- AutoConfigurationImportSelector:导入自动配置类
自动配置流程
┌─────────────────────────────────────────────────────────────────────┐
│ 自动配置执行流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ @SpringBootApplication │
│ │ │
│ ▼ │
│ @EnableAutoConfiguration │
│ │ │
│ ▼ │
│ AutoConfigurationImportSelector │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 读取 META-INF/spring/ │ │
│ │ AutoConfiguration.imports │ │
│ │ (Spring Boot 3.x) │ │
│ │ 或 │ │
│ │ META-INF/spring.factories │ │
│ │ (Spring Boot 2.x) │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 加载所有自动配置类 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ @Conditional 条件判断 │ │
│ │ - @ConditionalOnClass │ │
│ │ - @ConditionalOnBean │ │
│ │ - @ConditionalOnProperty │ │
│ │ - ... │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 条件满足 → 注册 Bean │
│ 条件不满足 → 跳过 │
│ │
└─────────────────────────────────────────────────────────────────────┘
SPI 机制
Spring Boot 使用 Java SPI(Service Provider Interface)机制加载自动配置类。
Spring Boot 3.x 配置文件:
位置:META-INF/spring/AutoConfiguration.imports
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
...
Spring Boot 2.x 配置文件:
位置:META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
...
条件注解
条件注解是自动配置的核心,用于判断某个配置是否应该生效。
常用条件注解
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | classpath 中存在指定类时生效 |
@ConditionalOnMissingClass | classpath 中不存在指定类时生效 |
@ConditionalOnBean | 容器中存在指定 Bean 时生效 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty | 指定属性满足条件时生效 |
@ConditionalOnWebApplication | 当前是 Web 应用时生效 |
@ConditionalOnExpression | SpEL 表达式为 true 时生效 |
@ConditionalOnJava | Java 版本满足条件时生效 |
条件注解示例
@ConditionalOnClass
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
// 只有当 classpath 中存在 DataSource 类时,才会加载此配置
}
解释:如果项目中没有引入 JDBC 相关依赖,DataSource 类不存在,此配置不会生效。
@ConditionalOnMissingBean
@Configuration
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
解释:只有当容器中没有其他 ViewResolver 时,才会创建默认的 ViewResolver。这允许开发者自定义 ViewResolver 来覆盖默认配置。
@ConditionalOnProperty
@Configuration
@ConditionalOnProperty(
prefix = "app.cache",
name = "enabled",
havingValue = "true",
matchIfMissing = false
)
public class CacheAutoConfiguration {
// 当 app.cache.enabled=true 时生效
}
属性说明:
| 属性 | 说明 |
|---|---|
prefix | 属性前缀 |
name | 属性名 |
havingValue | 期望的属性值 |
matchIfMissing | 属性不存在时是否匹配 |
条件注解组合使用
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnMissingBean({ SqlSessionFactory.class, SqlSessionTemplate.class })
@EnableConfigurationProperties(MybatisProperties.class)
public class MybatisAutoConfiguration {
// 只有当:
// 1. classpath 中存在 SqlSessionFactory 和 SqlSessionFactoryBean
// 2. 容器中有且仅有一个 DataSource Bean
// 3. 容器中不存在 SqlSessionFactory 和 SqlSessionTemplate
// 时才会加载此配置
}
自动配置类分析
以 WebMvcAutoConfiguration 为例分析:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
// ...
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc.contentnegotiation", name = "favor-parameter", havingValue = "true")
public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
// ...
}
// ... 更多配置
}
逐层解析:
| 注解 | 含义 |
|---|---|
@ConditionalOnWebApplication(type = Type.SERVLET) | 当前是 Servlet Web 应用 |
@ConditionalOnClass({ Servlet.class, ... }) | classpath 中存在这些类 |
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) | 没有自定义的 MVC 配置 |
@AutoConfigureOrder | 配置加载顺序 |
@AutoConfigureAfter | 在指定配置之后加载 |
查看自动配置报告
方式一:启动参数
java -jar app.jar --debug
或配置文件:
debug=true
方式二:使用 Actuator
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置暴露端点:
management:
endpoints:
web:
exposure:
include: conditions
访问:http://localhost:8080/actuator/conditions
自动配置报告示例
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:(匹配成功的配置)
-----------------
WebMvcAutoConfiguration matched:
- @ConditionalOnClass found required classes... (@ConditionalOnClass)
Negative matches:(未匹配的配置)
-----------------
DataSourceAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.sql.DataSource'
禁用特定自动配置
方式一:@SpringBootApplication 注解
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
// ...
}
方式二:配置文件
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
方式三:@EnableAutoConfiguration 注解
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
// ...
}
自定义自动配置
创建自动配置类
package com.example.autoconfigure;
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "my.service", name = "enabled", havingValue = "true", matchIfMissing = true)
public MyService myService(MyServiceProperties properties) {
return new MyService(properties);
}
}
创建配置属性类
package com.example.autoconfigure;
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private boolean enabled = true;
private String name = "default";
// getters and setters
}
注册自动配置
在 META-INF/spring/AutoConfiguration.imports 中添加:
com.example.autoconfigure.MyServiceAutoConfiguration
使用自定义自动配置
添加依赖后,在 application.yml 中配置:
my:
service:
enabled: true
name: my-service
起步依赖原理
什么是起步依赖?
起步依赖(Starter)是一组依赖描述符的集合,它将常用的依赖组合在一起,简化依赖管理。
常用起步依赖
| 依赖 | 包含内容 |
|---|---|
spring-boot-starter-web | Spring MVC、Tomcat、Jackson、验证 |
spring-boot-starter-data-jpa | Spring Data JPA、Hibernate |
spring-boot-starter-security | Spring Security |
spring-boot-starter-test | JUnit、Mockito、AssertJ |
spring-boot-starter-actuator | 生产级监控功能 |
起步依赖结构
spring-boot-starter-web/
├── pom.xml
│ ├── spring-boot-starter(核心)
│ ├── spring-boot-starter-tomcat(容器)
│ ├── spring-web(Spring Web)
│ └── spring-webmvc(Spring MVC)
查看依赖树
mvn dependency:tree
输出示例:
com.example:demo:jar:0.0.1-SNAPSHOT
└── org.springframework.boot:spring-boot-starter-web:jar:3.2.0
├── org.springframework.boot:spring-boot-starter:jar:3.2.0
├── org.springframework.boot:spring-boot-starter-tomcat:jar:3.2.0
├── org.springframework:spring-web:jar:6.1.0
└── org.springframework:spring-webmvc:jar:6.1.0
最佳实践
1. 不要过度自定义
只有当默认配置不满足需求时才自定义:
// 不推荐:不必要的自定义
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// 推荐:使用配置文件覆盖
# application.properties
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
2. 理解条件注解优先级
条件注解的顺序很重要:
// 正确:先检查类是否存在,再检查 Bean
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
public class MyDataSourceConfiguration {
// ...
}
3. 合理使用 @ConditionalOnMissingBean
允许用户覆盖默认配置:
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new DefaultMyService();
}
4. 利用配置属性
@ConfigurationProperties(prefix = "my.app")
public class MyAppProperties {
private String name;
private boolean enabled = true;
// ...
}
小结
本章我们学习了:
- 自动配置概念:理解 Spring Boot 自动配置的作用和优势
- 工作原理:SPI 机制和条件注解的结合
- 条件注解:各种条件注解的使用方法和场景
- 配置类分析:分析自动配置类的加载条件
- 查看配置报告:调试和理解自动配置
- 自定义自动配置:创建自己的自动配置
- 起步依赖:理解起步依赖的结构和原理
练习
- 启动一个 Spring Boot 应用,查看自动配置报告
- 分析
DataSourceAutoConfiguration的条件判断逻辑 - 尝试禁用某个自动配置类
- 创建一个自定义的自动配置
- 查看某个起步依赖的依赖树