目标:讲清楚 Spring Boot 3 自动配置的工作机制,以及 Forge Admin 18 个 Starter 如何通过组合实现"引入即生效"。
企业后台框架的核心痛点是模块多了配置就乱。一个 Starter 要能用的前提是:
pom.xml 依赖,不需要手写任何 Java Configapplication.yml 调整Spring Boot 的自动配置就是这套"少写配置,多做组合"的工程范式。
Forge Admin 有 21 个 Starter 和 6 个 Plugin,如果每个都要用户手动写 @Configuration 和 @Bean,光是配置代码就得上千行。自动配置把这个成本压到了零。
AutoConfiguration.importsSpring Boot 3.x 使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件注册自动配置类(取代老版本 spring.factories):
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.mdframe.forge.starter.core.config.ExceptionAutoConfiguration
com.mdframe.forge.starter.core.config.JacksonConfig
Spring Boot 启动时会扫描 classpath 下所有 jar 中的这个文件,加载并执行其中声明的配置类。
Spring Boot 启动
→ 扫描所有 jar 的 AutoConfiguration.imports 文件
→ 加载声明的 @AutoConfiguration / @Configuration 类
→ 按 @AutoConfigureOrder 排序
→ 逐个评估 @Conditional 条件注解
→ 条件满足 → 执行 @Bean 方法注册
→ 条件不满足 → 跳过该类
spring.factories 的对比AutoConfiguration.imports (3.x) |
spring.factories (2.x) |
|
|---|---|---|
| 文件路径 | META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
META-INF/spring.factories |
| 每行一个类 | 是,一行一个全限定类名 | 否,key=value 格式 |
| 注解要求 | 必须用 @AutoConfiguration |
@Configuration 即可 |
| 专用性 | 仅用于自动配置 | 可注册各种工厂类 |
注意:Forge Admin 同时保留了一个 spring.factories,位于 forge-starter-config,用于注册 EnvironmentPostProcessor(这不是自动配置类,所以不能用 AutoConfiguration.imports):
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mdframe.forge.starter.property.DbPropertySourcePostProcessor
Spring Boot 提供了一系列 @Conditional 注解,Forge Admin 中使用的按频率排序:
| 注解 | 用途 | 项目使用次数 |
|---|---|---|
@ConditionalOnMissingBean |
用户可自定义 Bean 覆盖默认实现 | 24+ |
@ConditionalOnProperty |
通过 yml 配置控制功能开关 | 12 |
@ConditionalOnBean |
依赖其他 Bean 存在才生效 | 6 |
@ConditionalOnClass |
依赖某类在 classpath 才生效 | 2 |
@ConditionalOnWebApplication |
仅在 Web 环境中生效 | 1 |
@ConditionalOnMissingBean — 允许用户覆盖最常见模式:框架提供一个默认实现,但用户可以通过声明自己的 Bean 来覆盖。
// forge-starter-tenant / TenantAutoConfiguration
@Bean
@ConditionalOnMissingBean
public DefaultTenantLineHandler tenantLineHandler(
@Autowired(required = false) TenantTableChecker tenantTableChecker) {
return new DefaultTenantLineHandler(tenantTableChecker);
}
如果用户在自己项目里也声明了 @Bean public TenantLineHandler myHandler(),框架的这个 Bean 就不会创建。
@ConditionalOnProperty — 功能开关通过 yml 配置控制整个模块或子功能:
// 类级:控制整个模块
@AutoConfiguration
@ConditionalOnProperty(prefix = "forge.tenant", name = "enabled",
havingValue = "true", matchIfMissing = true)
public class TenantAutoConfiguration { ... }
// 方法级:控制子功能
@Bean
@ConditionalOnProperty(prefix = "forge.tenant",
name = "auto-detect-tenant-column", havingValue = "true", matchIfMissing = true)
public TenantTableChecker tenantTableChecker(DataSource dataSource) { ... }
关键约定:所有 Forge Starter 的 enabled 开关都设置 matchIfMissing = true,意味着不配置默认启用,做到真正的"引入即生效"。
@ConditionalOnBean — 级联依赖典型场景在 forge-starter-crypto,防重放攻击过滤器依赖于 Redis 缓存服务:
// 第一层:Redis 缓存存在才创建 Token 缓存
@Bean
@ConditionalOnBean(ICacheService.class)
public ReplayTokenCache replayTokenCache(ICacheService cacheService) { ... }
// 第二层:Token 缓存存在 + 配置开关打开,才注册过滤器
@Bean
@ConditionalOnBean(ReplayTokenCache.class)
@ConditionalOnProperty(prefix = "forge.crypto", name = "enableReplayProtection", havingValue = "true")
public FilterRegistrationBean<ReplayAttackFilter> replayAttackFilter(
ReplayTokenCache replayTokenCache) { ... }
@ConditionalOnClass — 可选依赖forge-starter-idempotent 中,只有 classpath 存在 Redisson 时才启用分布式锁:
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(RedissonClient.class)
public LockManager lockManager(RedissonClient redissonClient) {
return new LockManager(redissonClient);
}
forge-starter-core 的异常处理器,无任何条件判断:
@Slf4j
@AutoConfiguration
public class ExceptionAutoConfiguration {
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
log.info("全局异常处理器已启用");
return new GlobalExceptionHandler();
}
}
forge-starter-tenant 的完整模式:
@AutoConfiguration ← 标记为自动配置类
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) ← 控制顺序
@EnableConfigurationProperties(TenantProperties.class) ← 属性绑定
@ConditionalOnProperty(prefix = "forge.tenant", ...) ← 条件开关
public class TenantAutoConfiguration {
@Bean @ConditionalOnMissingBean ← 用户可覆盖
public TenantLineHandler handler() { ... }
@Bean @ConditionalOnMissingBean(name = "xxx") ← 指定 bean name 匹配
public TenantLineInnerInterceptor interceptor() { ... }
}
forge-starter-orm 的 MybatisPlusConfig 是整个框架最关键的设计点:
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan("${mybatis-plus.mapperPackage}")
public class MybatisPlusConfig {
@Autowired(required = false)
private List<InnerInterceptor> innerInterceptors; // 自动收集所有拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 1. 先注册其他 Starter 提供的拦截器(租户、数据权限等)
if (innerInterceptors != null && !innerInterceptors.isEmpty()) {
innerInterceptors.forEach(interceptor::addInnerInterceptor);
}
// 2. 再注册分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 3. 最后注册乐观锁
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
return interceptor;
}
}
这是"组合优于配置"的典范:ORM 模块不需要知道有哪些拦截器,只需声明
List<InnerInterceptor>,其他 Starter 注册的拦截器会自动注入进来。新增一个拦截器模块无需改动 ORM 的任何代码。
forge-starter-idempotent 把多种幂等策略注册到一个 Map,由 AOP 切面选择:
@Bean
public Map<IdempotentStrategy, IdempotentStrategyHandler> strategyHandlers(
IdempotentStrategyHandler strictStrategyHandler,
IdempotentStrategyHandler returnCacheStrategyHandler,
IdempotentStrategyHandler tokenRequiredStrategyHandler) {
Map<IdempotentStrategy, IdempotentStrategyHandler> handlers = new HashMap<>();
handlers.put(IdempotentStrategy.STRICT, strictStrategyHandler);
handlers.put(IdempotentStrategy.RETURN_CACHE, returnCacheStrategyHandler);
handlers.put(IdempotentStrategy.TOKEN_REQUIRED, tokenRequiredStrategyHandler);
return handlers;
}
forge-starter-web 是整个框架最特殊的"Starter"——它没有任何 Java 代码,仅靠 POM 聚合依赖:
<dependencies>
<dependency>org.springframework.boot:spring-boot-starter-web</dependency>
<dependency>org.springframework.boot:spring-boot-starter-undertow</dependency>
<dependency>org.springframework.boot:spring-boot-starter-actuator</dependency>
</dependencies>
引入 forge-starter-web,自动获得 Undertow + Actuator + 全局异常处理(来自 forge-starter-core),用户零配置。
┌─────────────────────────────────────────────────────┐
│ AutoConfiguration.imports 注册 (18 个模块) │
│ ┌─ @AutoConfiguration │
│ │ ├─ @ConditionalOnProperty → 开关控制 │
│ │ ├─ @ConditionalOnMissingBean → 用户可覆盖 │
│ │ └─ @AutoConfigureOrder → 排序控制 │
│ └─ @Configuration (auth 模块) │
├─────────────────────────────────────────────────────┤
│ @ComponentScan 自动发现 (log 模块) │
├─────────────────────────────────────────────────────┤
│ 纯 POM 聚合 (web 模块) │
├─────────────────────────────────────────────────────┤
│ spring.factories (config 模块) │
│ └─ EnvironmentPostProcessor │
└─────────────────────────────────────────────────────┘
| Starter | 注册类 | 装配方式 |
|---|---|---|
| forge-starter-core | ExceptionAutoConfiguration, JacksonConfig |
@AutoConfiguration |
| forge-starter-orm | MybatisPlusConfig |
@Configuration + @MapperScan |
| forge-starter-cache | RedissonConfig |
@AutoConfiguration |
| forge-starter-tenant | TenantAutoConfiguration |
@AutoConfiguration + @ConditionalOnProperty |
| forge-starter-datascope | DataScopeAutoConfiguration |
@AutoConfiguration + @ConditionalOnProperty |
| forge-starter-crypto | CryptoAutoConfiguration, JacksonCryptoConfiguration |
@AutoConfiguration |
| forge-starter-idempotent | IdempotentAutoConfiguration |
@AutoConfiguration + @ConditionalOnProperty |
| forge-starter-id | IdAutoConfiguration |
@AutoConfiguration |
| forge-starter-excel | ExcelAutoConfiguration |
@AutoConfiguration |
| forge-starter-file | FileAutoConfiguration |
@AutoConfiguration |
| forge-starter-message | MessageAutoConfiguration |
@AutoConfiguration |
| forge-starter-social | SocialAutoConfiguration |
@AutoConfiguration |
| forge-starter-trans | TransAutoConfiguration |
@AutoConfiguration |
| forge-starter-websocket | WebSocketConfig |
@Configuration |
| forge-starter-api-config | ApiConfigAutoConfiguration |
@AutoConfiguration |
| forge-starter-config | PropertyRefreshAutoConfiguration, ConfigAutoConfiguration |
@AutoConfiguration + spring.factories |
| forge-starter-auth | SaTokenConfig |
@Configuration + @ComponentScan |
| forge-starter-web | (无 Java 代码) | 纯 POM 聚合 |
@Configuration — 引入 Maven 依赖即生效List<InnerInterceptor> 自动收集@AutoConfigureOrder 在框架层排好了| 约定 | 体现 |
|---|---|
| 默认启用 | 所有 forge.*.enabled 默认 matchIfMissing = true |
| 统一属性前缀 | 所有配置以 forge. 开头,前缀 = 模块名 |
| 统一命名规范 | forge-starter-xxx → 配置前缀 forge.xxx |
| 用户覆盖优先 | 所有核心 Bean 加 @ConditionalOnMissingBean |
forge-starter-orm 通过 List<InnerInterceptor> 组合租户、数据权限、分页、乐观锁forge-starter-web 通过 POM 聚合 Undertow、Actuator、Jackson 等forge-starter-idempotent 通过 Map 组合多种策略处理器如果要新增一个 forge-starter-notification,标准步骤:
forge-framework/forge-starter-parent/forge-starter-notification/java
@ConfigurationProperties(prefix = "forge.notification")
@Data
public class NotificationProperties {
private boolean enabled = true;
private String channel = "sms";
}
编写自动配置类:
@AutoConfiguration
@EnableConfigurationProperties(NotificationProperties.class)
@ConditionalOnProperty(prefix = "forge.notification", name = "enabled",
havingValue = "true", matchIfMissing = true)
public class NotificationAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NotificationService notificationService(NotificationProperties props) {
return new DefaultNotificationService(props);
}
}
注册到 imports 文件:在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中添加一行:
com.mdframe.forge.starter.notification.config.NotificationAutoConfiguration
在父 POM 中声明模块:forge-starter-parent/pom.xml 的 <modules> 添加。
| 问题 | 原因 | 解决 |
|---|---|---|
| 自动配置类不生效 | 没写 AutoConfiguration.imports 文件,或文件路径错误 |
检查 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
@ConditionalOnProperty 不工作 |
属性名拼写错误或大小写不一致 | kebab-case 转 camelCase 规则:enable-replay-protection → enableReplayProtection |
@Autowired(required = false) 注入为 null |
被注入的 Bean 没有被其他模块注册 | 确认依赖的 Starter 已引入,且其自动配置已生效 |
| 两个拦截器顺序不对 | @AutoConfigureOrder 值设置不当 |
提高 order 值使该配置延迟加载 |
| 用户自定义 Bean 没覆盖默认 | 忘了加 @ConditionalOnMissingBean |
在默认 Bean 方法上加该注解 |
spring.factories 在新版本不生效 |
Spring Boot 3.x 已弃用 | 迁移到 AutoConfiguration.imports |
| 文件 | 说明 |
|---|---|
forge-framework/forge-starter-parent/*/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
各 Starter 的自动配置注册文件 |
forge-framework/forge-starter-parent/forge-starter-orm/src/main/java/com/mdframe/forge/starter/orm/config/MybatisPlusConfig.java |
拦截器组合的关键实现 |
forge-framework/forge-starter-parent/forge-starter-tenant/src/main/java/com/mdframe/forge/starter/tenant/config/TenantAutoConfiguration.java |
条件装配的标准模板 |
forge-framework/forge-starter-parent/forge-starter-crypto/src/main/java/com/mdframe/forge/starter/crypto/config/CryptoAutoConfiguration.java |
级联条件装配示例 |
forge-framework/forge-starter-parent/forge-starter-idempotent/src/main/java/com/mdframe/forge/starter/idempotent/config/IdempotentAutoConfiguration.java |
策略模式装配示例 |