跳到主要内容

自动配置原理

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 {
// ...
}

关键组件

  1. @AutoConfigurationPackage:将主配置类所在包作为自动配置包
  2. 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,\
...

条件注解

条件注解是自动配置的核心,用于判断某个配置是否应该生效。

常用条件注解

注解作用
@ConditionalOnClassclasspath 中存在指定类时生效
@ConditionalOnMissingClassclasspath 中不存在指定类时生效
@ConditionalOnBean容器中存在指定 Bean 时生效
@ConditionalOnMissingBean容器中不存在指定 Bean 时生效
@ConditionalOnProperty指定属性满足条件时生效
@ConditionalOnWebApplication当前是 Web 应用时生效
@ConditionalOnExpressionSpEL 表达式为 true 时生效
@ConditionalOnJavaJava 版本满足条件时生效

条件注解示例

@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-webSpring MVC、Tomcat、Jackson、验证
spring-boot-starter-data-jpaSpring Data JPA、Hibernate
spring-boot-starter-securitySpring Security
spring-boot-starter-testJUnit、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;
// ...
}

小结

本章我们学习了:

  1. 自动配置概念:理解 Spring Boot 自动配置的作用和优势
  2. 工作原理:SPI 机制和条件注解的结合
  3. 条件注解:各种条件注解的使用方法和场景
  4. 配置类分析:分析自动配置类的加载条件
  5. 查看配置报告:调试和理解自动配置
  6. 自定义自动配置:创建自己的自动配置
  7. 起步依赖:理解起步依赖的结构和原理

练习

  1. 启动一个 Spring Boot 应用,查看自动配置报告
  2. 分析 DataSourceAutoConfiguration 的条件判断逻辑
  3. 尝试禁用某个自动配置类
  4. 创建一个自定义的自动配置
  5. 查看某个起步依赖的依赖树