Spring Boot 自动配置原理详解
一、知识概述
Spring Boot 的核心特性之一是自动配置(Auto-Configuration),它根据项目中的依赖和配置,自动配置 Spring 应用程序。这大大简化了 Spring 应用的开发,开发者只需引入相关依赖,Spring Boot 就会自动完成配置工作。
自动配置的核心机制:
- @EnableAutoConfiguration:启用自动配置
- spring.factories:自动配置类注册
- 条件注解:按需加载配置
- Starter 机制:依赖管理
理解自动配置原理,有助于更好地使用 Spring Boot,并能够自定义 Starter。
二、知识点详细讲解
2.1 @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 {
// ...
}
三个核心注解:
- @SpringBootConfiguration:等价于 @Configuration
- @EnableAutoConfiguration:启用自动配置
- @ComponentScan:组件扫描
2.2 @EnableAutoConfiguration 工作原理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class) // 导入自动配置选择器
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
核心组件:
- @AutoConfigurationPackage:注册主配置类所在包
- AutoConfigurationImportSelector:导入自动配置类
2.3 自动配置类发现机制
启动应用
↓
AutoConfigurationImportSelector
↓
加载 META-INF/spring.factories
↓
获取 EnableAutoConfiguration 配置的类
↓
条件过滤
↓
注册符合条件的配置类
spring.factories 文件格式
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
2.4 条件注解
Spring Boot 提供了丰富的条件注解来控制配置类的加载:
| 条件注解 | 说明 |
|---|---|
| @ConditionalOnClass | 类路径存在指定类时生效 |
| @ConditionalOnMissingClass | 类路径不存在指定类时生效 |
| @ConditionalOnBean | 容器中存在指定 Bean 时生效 |
| @ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 |
| @ConditionalOnProperty | 配置属性满足条件时生效 |
| @ConditionalOnResource | 资源存在时生效 |
| @ConditionalOnWebApplication | Web 应用时生效 |
| @ConditionalOnExpression | SpEL 表达式为 true 时生效 |
2.5 Starter 机制
Starter 是 Spring Boot 提供的依赖管理方案,一个 Starter 通常包含:
- 自动配置类:自动配置相关 Bean
- 依赖管理:传递相关依赖
- 配置属性:可配置的属性类
常用 Starter
| Starter | 说明 |
|---|---|
| spring-boot-starter-web | Web 开发 |
| spring-boot-starter-data-jpa | JPA 数据访问 |
| spring-boot-starter-data-redis | Redis |
| spring-boot-starter-security | 安全框架 |
| spring-boot-starter-actuator | 监控端点 |
2.6 配置属性绑定
Spring Boot 通过 @ConfigurationProperties 实现属性绑定:
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private int port;
// getter/setter
}
对应的配置:
app:
name: myapp
port: 8080
三、代码示例
3.1 自动配置示例
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.context.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
// 数据源配置类
@Configuration
@ConditionalOnClass(javax.sql.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
private final DataSourceProperties properties;
public DataSourceAutoConfiguration(DataSourceProperties properties) {
this.properties = properties;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return createDataSource();
}
private DataSource createDataSource() {
// 创建数据源
return null;
}
}
// 数据源属性类
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize = 5;
private int maxActive = 20;
// getter/setter
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getDriverClassName() { return driverClassName; }
public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; }
public int getInitialSize() { return initialSize; }
public void setInitialSize(int initialSize) { this.initialSize = initialSize; }
public int getMaxActive() { return maxActive; }
public void setMaxActive(int maxActive) { this.maxActive = maxActive; }
}
3.2 条件注解示例
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.context.annotation.*;
import org.springframework.beans.factory.annotation.Value;
@Configuration
public class ConditionalConfig {
// 当类路径存在 HikariDataSource 时创建
@Bean
@ConditionalOnClass(com.zaxxer.hikari.HikariDataSource.class)
public DataSource hikariDataSource() {
System.out.println("创建 Hikari 数据源");
return null; // 实际创建逻辑
}
// 当没有其他 DataSource Bean 时创建默认数据源
@Bean
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public DataSource defaultDataSource() {
System.out.println("创建默认数据源");
return null;
}
// 根据配置属性决定是否创建
@Bean
@ConditionalOnProperty(
prefix = "app.cache",
name = "enabled",
havingValue = "true",
matchIfMissing = true // 如果属性不存在,默认匹配
)
public CacheManager cacheManager() {
System.out.println("创建缓存管理器");
return null;
}
// Web 应用时创建
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public WebHandler webHandler() {
System.out.println("创建 Web 处理器");
return null;
}
// 非 Web 应用时创建
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.NONE)
public CommandLineRunner cliRunner() {
System.out.println("创建命令行运行器");
return args -> System.out.println("命令行应用启动");
}
// 表达式条件
@Bean
@ConditionalOnExpression("${app.feature.enabled:false} and '${app.mode}'.equals('production')")
public FeatureService productionFeatureService() {
System.out.println("创建生产环境特性服务");
return new FeatureService();
}
// 资源存在时创建
@Bean
@ConditionalOnResource(resources = "classpath:config.properties")
public ConfigLoader configLoader() {
System.out.println("创建配置加载器");
return new ConfigLoader();
}
// 组合条件
@Bean
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
@ConditionalOnMissingBean(RedisClient.class)
public RedisClient redisClient() {
System.out.println("创建 Redis 客户端");
return new RedisClient();
}
}
// 辅助类
public class CacheManager {}
public class WebHandler {}
public class FeatureService {}
public class ConfigLoader {}
public class RedisClient {}
public interface DataSource {}
3.3 自定义 Starter
项目结构
my-spring-boot-starter/
├── src/main/java/
│ └── com/example/
│ ├── autoconfigure/
│ │ ├── MyAutoConfiguration.java
│ │ └── MyProperties.java
│ └── MyService.java
└── src/main/resources/
└── META-INF/
└── spring.factories
└── pom.xml
自动配置类
package com.example.autoconfigure;
import com.example.MyService;
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
private final MyProperties properties;
public MyAutoConfiguration(MyProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService(
properties.getName(),
properties.getEnabled()
);
}
}
属性类
package com.example.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
/**
* 服务名称
*/
private String name = "default";
/**
* 是否启用
*/
private boolean enabled = true;
/**
* 超时时间(毫秒)
*/
private int timeout = 3000;
/**
* 重试次数
*/
private int retryCount = 3;
// getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean getEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
public int getRetryCount() { return retryCount; }
public void setRetryCount(int retryCount) { this.retryCount = retryCount; }
}
服务类
package com.example;
public class MyService {
private final String name;
private final boolean enabled;
public MyService(String name, boolean enabled) {
this.name = name;
this.enabled = enabled;
}
public String sayHello() {
if (!enabled) {
return "Service is disabled";
}
return "Hello from " + name + "!";
}
public String getName() {
return name;
}
public boolean isEnabled() {
return enabled;
}
}
spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyAutoConfiguration
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 可选:配置处理器,生成配置元数据 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.2.0</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
使用自定义 Starter
<!-- 在其他项目中引入 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
# application.yml
my:
service:
name: my-app
enabled: true
timeout: 5000
retry-count: 5
@Service
public class AppService {
private final MyService myService;
public AppService(MyService myService) {
this.myService = myService;
}
public void doSomething() {
System.out.println(myService.sayHello());
}
}
3.4 配置属性绑定示例
import org.springframework.boot.context.properties.*;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.*;
// 嵌套属性
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
@NotBlank
private String name;
@Min(1)
@Max(65535)
private int port = 8080;
private final Security security = new Security();
private final Database database = new Database();
private final Cache cache = new Cache();
// getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public Security getSecurity() { return security; }
public Database getDatabase() { return database; }
public Cache getCache() { return cache; }
// 嵌套类
public static class Security {
private boolean enabled = true;
private String secretKey;
// getter/setter
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getSecretKey() { return secretKey; }
public void setSecretKey(String secretKey) { this.secretKey = secretKey; }
}
public static class Database {
private String url;
private String username;
private String password;
private int poolSize = 10;
// getter/setter
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getPoolSize() { return poolSize; }
public void setPoolSize(int poolSize) { this.poolSize = poolSize; }
}
public static class Cache {
private boolean enabled = false;
private String type = "redis";
private long ttl = 3600;
// getter/setter
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public long getTtl() { return ttl; }
public void setTtl(long ttl) { this.ttl = ttl; }
}
}
// 启用配置属性
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class PropertiesConfig {
}
// 使用配置属性
@Service
public class ConfigurableService {
private final AppProperties properties;
public ConfigurableService(AppProperties properties) {
this.properties = properties;
}
public void printConfig() {
System.out.println("应用名称: " + properties.getName());
System.out.println("端口: " + properties.getPort());
System.out.println("安全配置: enabled=" + properties.getSecurity().isEnabled());
System.out.println("数据库URL: " + properties.getDatabase().getUrl());
System.out.println("缓存类型: " + properties.getCache().getType());
}
}
对应的配置文件
app:
name: my-application
port: 9090
security:
enabled: true
secret-key: my-secret-key
database:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
pool-size: 20
cache:
enabled: true
type: redis
ttl: 7200
3.5 自动配置排除
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
// 方式1:通过注解排除
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 方式2:通过配置文件排除
// application.yml
/*
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
*/
// 方式3:通过条件注解控制
@Configuration
@ConditionalOnProperty(prefix = "app.datasource", name = "enabled", havingValue = "true")
public class CustomDataSourceAutoConfiguration {
// 只有当 app.datasource.enabled=true 时才加载
}
3.6 自动配置调试
import org.springframework.boot.autoconfigure.*;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Configuration
public class AutoConfigDebug {
// 打印已加载的自动配置
@Bean
public CommandLineRunner autoConfigReporter(AutoConfigurationReport report, Environment env) {
return args -> {
System.out.println("\n=== 自动配置报告 ===");
System.out.println("调试模式: " + env.getProperty("debug"));
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
System.out.println("\n配置类: " + source);
outcomes.forEach(outcome -> {
String status = outcome.getOutcome().isMatch() ? "✓ 匹配" : "✗ 不匹配";
System.out.println(" " + status + " - " + outcome.getOutcome().getMessage());
});
});
};
}
}
// 启用调试模式
// 方式1:启动参数
// java -jar app.jar --debug
// 方式2:配置文件
// debug: true
// 方式3:使用 actuator
// GET /actuator/conditions
四、实战应用场景
4.1 实现多数据源自动配置
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.*;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
// 多数据源属性
@ConfigurationProperties(prefix = "app.datasource")
public class MultiDataSourceProperties {
private Map<String, DataSourceConfig> sources = new HashMap<>();
public Map<String, DataSourceConfig> getSources() {
return sources;
}
public static class DataSourceConfig {
private String url;
private String username;
private String password;
private String driverClassName;
// getter/setter
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getDriverClassName() { return driverClassName; }
public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; }
}
}
// 多数据源自动配置
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(MultiDataSourceProperties.class)
public class MultiDataSourceAutoConfiguration {
private final MultiDataSourceProperties properties;
public MultiDataSourceAutoConfiguration(MultiDataSourceProperties properties) {
this.properties = properties;
}
@Bean
@Primary
@ConditionalOnMissingBean
public DataSource primaryDataSource() {
MultiDataSourceProperties.DataSourceConfig config =
properties.getSources().get("primary");
if (config == null) {
throw new IllegalStateException("未配置主数据源");
}
return createDataSource(config);
}
@Bean
@ConditionalOnProperty(prefix = "app.datasource.sources", name = "secondary")
public DataSource secondaryDataSource() {
MultiDataSourceProperties.DataSourceConfig config =
properties.getSources().get("secondary");
return createDataSource(config);
}
private DataSource createDataSource(MultiDataSourceProperties.DataSourceConfig config) {
// 创建数据源逻辑
return null;
}
}
配置示例
app:
datasource:
sources:
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
4.2 实现缓存自动配置
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.*;
import org.springframework.context.annotation.*;
// 缓存属性
@ConfigurationProperties(prefix = "app.cache")
public class CacheProperties {
private boolean enabled = true;
private String type = "redis";
private long defaultTtl = 3600;
private int maxSize = 1000;
// getter/setter
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public long getDefaultTtl() { return defaultTtl; }
public void setDefaultTtl(long defaultTtl) { this.defaultTtl = defaultTtl; }
public int getMaxSize() { return maxSize; }
public void setMaxSize(int maxSize) { this.maxSize = maxSize; }
}
// 缓存接口
public interface CacheService {
void put(String key, Object value);
void put(String key, Object value, long ttl);
Object get(String key);
void delete(String key);
void clear();
}
// Redis 缓存实现
public class RedisCacheService implements CacheService {
private final long defaultTtl;
public RedisCacheService(long defaultTtl) {
this.defaultTtl = defaultTtl;
}
@Override
public void put(String key, Object value) {
put(key, value, defaultTtl);
}
@Override
public void put(String key, Object value, long ttl) {
System.out.println("Redis 缓存写入: " + key);
}
@Override
public Object get(String key) {
System.out.println("Redis 缓存读取: " + key);
return null;
}
@Override
public void delete(String key) {
System.out.println("Redis 缓存删除: " + key);
}
@Override
public void clear() {
System.out.println("Redis 缓存清空");
}
}
// 本地缓存实现
public class LocalCacheService implements CacheService {
private final int maxSize;
private final Map<String, Object> cache = new LinkedHashMap<String, Object>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {
return size() > maxSize;
}
};
public LocalCacheService(int maxSize) {
this.maxSize = maxSize;
}
@Override
public void put(String key, Object value) {
cache.put(key, value);
}
@Override
public void put(String key, Object value, long ttl) {
cache.put(key, value);
}
@Override
public Object get(String key) {
return cache.get(key);
}
@Override
public void delete(String key) {
cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
}
// 自动配置类
@Configuration
@ConditionalOnClass(CacheService.class)
@EnableConfigurationProperties(CacheProperties.class)
public class CacheAutoConfiguration {
private final CacheProperties properties;
public CacheAutoConfiguration(CacheProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public CacheService redisCacheService() {
return new RedisCacheService(properties.getDefaultTtl());
}
@Bean
@ConditionalOnMissingBean(CacheService.class)
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true", matchIfMissing = true)
public CacheService localCacheService() {
return new LocalCacheService(properties.getMaxSize());
}
}
4.3 实现日志自动配置
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.*;
import org.springframework.context.annotation.*;
import org.springframework.beans.factory.annotation.Value;
// 日志属性
@ConfigurationProperties(prefix = "app.logging")
public class LoggingProperties {
private boolean enabled = true;
private String level = "INFO";
private boolean includeRequest = true;
private boolean includeResponse = true;
private int maxPayloadLength = 1000;
// getter/setter
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
public boolean isIncludeRequest() { return includeRequest; }
public void setIncludeRequest(boolean includeRequest) { this.includeRequest = includeRequest; }
public boolean isIncludeResponse() { return includeResponse; }
public void setIncludeResponse(boolean includeResponse) { this.includeResponse = includeResponse; }
public int getMaxPayloadLength() { return maxPayloadLength; }
public void setMaxPayloadLength(int maxPayloadLength) { this.maxPayloadLength = maxPayloadLength; }
}
// 日志服务
public class LoggingService {
private final LoggingProperties properties;
public LoggingService(LoggingProperties properties) {
this.properties = properties;
}
public void logRequest(String method, String uri, String body) {
if (!properties.isEnabled() || !properties.isIncludeRequest()) {
return;
}
String truncatedBody = truncate(body, properties.getMaxPayloadLength());
System.out.println(String.format("[%s] %s %s", properties.getLevel(), method, uri));
if (body != null) {
System.out.println("Request Body: " + truncatedBody);
}
}
public void logResponse(int status, String body) {
if (!properties.isEnabled() || !properties.isIncludeResponse()) {
return;
}
String truncatedBody = truncate(body, properties.getMaxPayloadLength());
System.out.println("Response Status: " + status);
System.out.println("Response Body: " + truncatedBody);
}
private String truncate(String str, int maxLength) {
if (str == null) return null;
if (str.length() <= maxLength) return str;
return str.substring(0, maxLength) + "...";
}
}
// 自动配置类
@Configuration
@ConditionalOnClass(LoggingService.class)
@EnableConfigurationProperties(LoggingProperties.class)
public class LoggingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "app.logging", name = "enabled", havingValue = "true", matchIfMissing = true)
public LoggingService loggingService(LoggingProperties properties) {
return new LoggingService(properties);
}
@Bean
@ConditionalOnBean(LoggingService.class)
@ConditionalOnWebApplication
public LoggingFilter loggingFilter(LoggingService loggingService) {
return new LoggingFilter(loggingService);
}
}
// 日志过滤器
public class LoggingFilter implements jakarta.servlet.Filter {
private final LoggingService loggingService;
public LoggingFilter(LoggingService loggingService) {
this.loggingService = loggingService;
}
@Override
public void doFilter(jakarta.servlet.ServletRequest request,
jakarta.servlet.ServletResponse response,
jakarta.servlet.FilterChain chain)
throws IOException, jakarta.servlet.ServletException {
jakarta.servlet.http.HttpServletRequest httpRequest =
(jakarta.servlet.http.HttpServletRequest) request;
loggingService.logRequest(
httpRequest.getMethod(),
httpRequest.getRequestURI(),
null
);
chain.doFilter(request, response);
}
}
五、总结与最佳实践
自动配置开发步骤
-
创建自动配置类
- 使用 @Configuration 标记
- 添加条件注解
- 定义 Bean
-
创建属性类
- 使用 @ConfigurationProperties
- 定义可配置属性
- 提供默认值
-
注册自动配置
- 创建 spring.factories
- 添加 EnableAutoConfiguration 配置
-
创建 Starter 模块
- 管理依赖
- 提供文档
最佳实践
-
条件注解使用
- 使用最合适的条件注解
- 组合条件时注意顺序
- 提供 matchIfMissing 默认值
-
属性设计
- 提供合理的默认值
- 使用嵌套类组织属性
- 添加文档注释
-
Bean 定义
- 使用 @ConditionalOnMissingBean 允许覆盖
- 考虑 Bean 的创建顺序
- 避免循环依赖
-
文档和测试
- 提供配置元数据
- 编写自动配置测试
- 提供使用文档
Spring Boot 自动配置是简化 Spring 应用开发的核心机制。理解其原理和最佳实践,能够帮助开发者更好地使用 Spring Boot,并能开发出高质量的 Starter 组件。