Spring Boot 3自动配置条件注解原理与自定义Starter开发

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/

(0)
小编小编
上一篇 13小时前
下一篇 13小时前

相关推荐