Spring Boot自动配置加载机制
Spring Boot自动配置通过spring.factories(Spring Boot 2.x)或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(Spring Boot 3.x)声明自动配置类。Spring Boot 3对该机制做了重大调整,弃用spring.factories中的EnableAutoConfiguration键值,改为独立的imports文件格式,每个类名占一行,加载效率更高且更易于IDE索引。
自动配置类的加载过程:应用启动时,AutoConfigurationImportSelector扫描classpath下所有jar包的imports文件,读取自动配置类列表,然后通过条件注解过滤,只保留满足条件的配置类进行实例化。整个机制的关键在于条件判断逻辑,Spring Boot提供了丰富的条件注解:
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "app.datasource", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties props) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(props.getUrl());
ds.setUsername(props.getUsername());
ds.setPassword(props.getPassword());
ds.setMaximumPoolSize(props.getMaxPoolSize());
return ds;
}
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
@ConditionalOnClass检查classpath是否存在指定类,@ConditionalOnProperty检查配置文件中的属性值,@ConditionalOnMissingBean确保只有用户未自定义该Bean时才创建默认实现,@ConditionalOnBean要求指定Bean存在时才生效。这些注解的组合实现了”约定优于配置”的核心思想——提供合理默认值,同时允许用户覆盖。
自定义Starter开发实战
开发一个自定义Starter需要拆分两个模块:autoconfigure模块包含自动配置逻辑,starter模块仅包含依赖声明(pom中引入autoconfigure模块和相关第三方库)。这种分离允许用户按需引入——只需要依赖管理用starter,只需要配置逻辑用autoconfigure。
以下是一个RateLimiter限流器Starter的完整实现。首先定义配置属性类:
@ConfigurationProperties(prefix = "ratelimiter")
public class RateLimiterProperties {
private boolean enabled = true;
private int defaultLimit = 100;
private int defaultPeriod = 60;
private Map<String, LimitRule> rules = new HashMap<>();
// Getters and Setters
public static class LimitRule {
private int limit;
private int period;
// Getters and Setters
}
}
定义核心限流服务:
public class RateLimiterService {
private final RateLimiterProperties properties;
private final Map<String, TokenBucket> buckets = new ConcurrentHashMap<>();
public RateLimiterService(RateLimiterProperties properties) {
this.properties = properties;
}
public boolean tryAcquire(String key) {
LimitRule rule = properties.getRules().get(key);
int limit = rule != null ? rule.getLimit() : properties.getDefaultLimit();
int period = rule != null ? rule.getPeriod() : properties.getDefaultPeriod();
TokenBucket bucket = buckets.computeIfAbsent(
key + ":" + period,
k -> new TokenBucket(limit, period)
);
return bucket.tryConsume();
}
private static class TokenBucket {
private final int capacity;
private final int periodSeconds;
private final AtomicInteger tokens;
private volatile long lastRefillTime;
TokenBucket(int capacity, int periodSeconds) {
this.capacity = capacity;
this.periodSeconds = periodSeconds;
this.tokens = new AtomicInteger(capacity);
this.lastRefillTime = System.currentTimeMillis();
}
boolean tryConsume() {
refill();
return tokens.getAndUpdate(t -> t > 0 ? t - 1 : 0) > 0;
}
private void refill() {
long now = System.currentTimeMillis();
long elapsed = (now - lastRefillTime) / 1000;
if (elapsed >= periodSeconds) {
tokens.set(capacity);
lastRefillTime = now;
}
}
}
}
自动配置类与条件装配实现
@AutoConfiguration
@ConditionalOnClass(RateLimiterService.class)
@EnableConfigurationProperties(RateLimiterProperties.class)
@ConditionalOnProperty(prefix = "ratelimiter", name = "enabled", havingValue = "true", matchIfMissing = true)
public class RateLimiterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RateLimiterService rateLimiterService(RateLimiterProperties properties) {
return new RateLimiterService(properties);
}
@Bean
@ConditionalOnMissingBean
public RateLimiterWebInterceptor rateLimiterWebInterceptor(
RateLimiterService rateLimiterService) {
return new RateLimiterWebInterceptor(rateLimiterService);
}
@Bean
public WebMvcConfigurer rateLimiterConfigurer(RateLimiterWebInterceptor interceptor) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
};
}
}
对应的Web拦截器实现按URL路径进行限流:
public class RateLimiterWebInterceptor implements HandlerInterceptor {
private final RateLimiterService rateLimiterService;
public RateLimiterWebInterceptor(RateLimiterService service) {
this.rateLimiterService = service;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String clientIp = request.getRemoteAddr();
String path = request.getRequestURI();
String key = clientIp + ":" + path;
if (!rateLimiterService.tryAcquire(key)) {
response.setStatus(429);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":429,\"message\":\"请求过于频繁,请稍后重试\"}");
return false;
}
return true;
}
}
注册imports文件与测试验证
Spring Boot 3要求在autoconfigure模块的资源目录下创建imports文件:
# 文件路径: src/main/resources/META-INF/spring/
# org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.yunthe.ratelimiter.autoconfigure.RateLimiterAutoConfiguration
使用该Starter的应用只需在application.yml中配置参数,无需任何Java配置代码:
ratelimiter:
enabled: true
default-limit: 100
default-period: 60
rules:
"/api/login":
limit: 5
period: 60
"/api/register":
limit: 3
period: 3600
验证自动配置是否生效,可以通过在应用启动时添加–debug参数查看自动配置报告。报告中会列出所有匹配和未匹配的自动配置类及原因。如果自定义Starter未出现在匹配列表中,检查imports文件路径和类名是否正确、条件注解是否满足。@ConditionalOnClass注解要求对应类在classpath中存在,如果starter未正确引入依赖,该条件将不满足导致自动配置被跳过。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot3-zi-dong-pei-zhi-tiao-jian-zhu-jie-yuan-li-yu-zi/