目录
-
- [一、什么是 Spring Boot 条件装配?](#一、什么是 Spring Boot 条件装配?)
- 二、@ConditionalOnClass:类存在时才装配
-
- [2.1 注解的作用](#2.1 注解的作用)
- [2.2 注解有哪些属性?](#2.2 注解有哪些属性?)
- [2.3 典型使用场景](#2.3 典型使用场景)
- [2.4 可以用在什么地方?](#2.4 可以用在什么地方?)
- [三、@ConditionalOnBean:指定 Bean 存在时才装配](#三、@ConditionalOnBean:指定 Bean 存在时才装配)
-
- [3.1 注解的作用](#3.1 注解的作用)
- [3.2 注解有哪些属性?](#3.2 注解有哪些属性?)
- [3.3 示例一:根据 Bean 类型判断](#3.3 示例一:根据 Bean 类型判断)
- [3.4 示例二:根据 Bean 名称判断](#3.4 示例二:根据 Bean 名称判断)
- [3.5 典型使用场景](#3.5 典型使用场景)
- [3.6 一个非常重要的注意事项](#3.6 一个非常重要的注意事项)
- 四、@ConditionalOnProperty:配置属性满足条件时才装配
-
- [4.1 注解的作用](#4.1 注解的作用)
- [4.2 注解有哪些属性?](#4.2 注解有哪些属性?)
- [4.3 havingValue 有什么用?](#4.3 havingValue 有什么用?)
- [4.4 matchIfMissing 有什么用?](#4.4 matchIfMissing 有什么用?)
- [4.5 实际业务示例:控制定时任务是否启用](#4.5 实际业务示例:控制定时任务是否启用)
- [4.6 一个使用限制:集合属性](#4.6 一个使用限制:集合属性)
- [五、@ConditionalOnMissingBean:没有指定 Bean 时才装配](#五、@ConditionalOnMissingBean:没有指定 Bean 时才装配)
-
- [5.1 注解的作用](#5.1 注解的作用)
- [5.2 注解有哪些属性?](#5.2 注解有哪些属性?)
- [5.3 示例一:提供默认服务实现](#5.3 示例一:提供默认服务实现)
- [5.4 示例二:用户自定义 Bean 覆盖默认实现](#5.4 示例二:用户自定义 Bean 覆盖默认实现)
- [5.5 为什么 Spring Boot 自动配置经常使用它?](#5.5 为什么 Spring Boot 自动配置经常使用它?)
- [5.6 为什么它经常写在 @Bean 方法上?](#5.6 为什么它经常写在 @Bean 方法上?)
- 六、四个注解的核心区别
- 七、这些注解到底作用在哪些地方?
-
- [7.1 作用在配置类上](#7.1 作用在配置类上)
- [7.2 作用在 @Bean 方法上](#7.2 作用在 @Bean 方法上)
- 八、实际开发中容易遇到的几个问题
-
- [8.1 为什么 @ConditionalOnClass 能检测到类,却仍然启动失败?](#8.1 为什么 @ConditionalOnClass 能检测到类,却仍然启动失败?)
- [8.2 为什么 @ConditionalOnBean 没有按预期生效?](#8.2 为什么 @ConditionalOnBean 没有按预期生效?)
- [8.3 为什么 @ConditionalOnProperty 不生效?](#8.3 为什么 @ConditionalOnProperty 不生效?)
- [8.4 为什么 @ConditionalOnMissingBean 仍然创建了默认 Bean?](#8.4 为什么 @ConditionalOnMissingBean 仍然创建了默认 Bean?)
- [九、如何查看 Spring Boot 条件装配结果?](#九、如何查看 Spring Boot 条件装配结果?)
- 十、总结
一、什么是 Spring Boot 条件装配?
在 Spring Boot 项目中,我们经常通过 @Configuration 定义配置类,通过 @Bean 向 Spring 容器中注册 Bean。
例如:
java
@Configuration
public class FeignConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
默认情况下,只要该配置类被 Spring 扫描或导入,myService() 方法就会参与 Bean 注册。
但在实际开发中,我们经常会遇到一些问题:
- 如果项目没有引入 Feign 依赖,是否还需要加载 Feign 相关配置?
- 如果某个 Bean 已经存在,是否还需要重复创建?
- 如果用户没有开启某项功能,是否应该加载对应的配置?
- 如果用户自己定义了某个 Bean,框架能否自动让出默认实现?
为了解决这些问题,Spring Boot 提供了一系列条件装配注解。
条件装配的核心思想是:只有满足指定条件,配置类或 Bean 定义才会生效。
常用的四个条件注解如下:
| 注解 | 核心判断条件 | 典型用途 |
|---|---|---|
@ConditionalOnClass |
指定的类是否存在于类路径中 | 依赖存在时才启用配置 |
@ConditionalOnBean |
Spring 容器中是否存在指定 Bean | 有前置组件时才启用功能 |
@ConditionalOnProperty |
配置文件中的属性是否满足条件 | 根据开关启用功能 |
@ConditionalOnMissingBean |
Spring 容器中是否不存在指定 Bean | 提供可被用户覆盖的默认实现 |
这些注解主要位于以下包中:
org.springframework.boot.autoconfigure.condition
注意:它们并不是只能用在自动配置类中,也可以应用于普通配置类或 @Bean 方法。
二、@ConditionalOnClass:类存在时才装配
2.1 注解的作用
@ConditionalOnClass 用于判断指定的类是否存在于应用程序的类路径(classpath)中。
只有当指定的类存在时,对应的配置类或 Bean 定义才会生效。否则整个配置类直接跳过、不注册任何 Bean。
例如:
java
@Configuration
@ConditionalOnClass(Feign.class)
public class FeignConfig {
@Bean
public MyFeignService myFeignService() {
return new MyFeignService();
}
}
这里的 Feign.class 通常指向:
java
feign.Feign
如果项目引入了包含该类的 Feign 依赖,那么这个条件可以满足,FeignConfig 才有资格参与配置,然后 FeignConfig 类里被 @Bean 注解的方法才会向 springboot 容器注入 MyFeignService 对象。
@ConditionalOnClass 它是一个"环境依赖的开关 ",让 FeignConfig 成为可选配置,避免在 "没用 Feign" 的项目中无谓报错或注入无用的 MyFeignService 对象。
如果项目没有相应依赖,条件不满足,配置类就不会生效。
这对于开发可复用的公共模块尤其重要。例如,公司有一个公共 Starter,其中包含 Feign 相关配置,但不是所有业务项目都使用 Feign。通过这个注解,就可以避免在不使用 Feign 的项目中启用相关配置。
2.2 注解有哪些属性?
常见属性如下:
| 属性 | 类型 | 作用 |
|---|---|---|
value |
Class<?>[] |
指定需要检测的类 |
name |
String[] |
通过全限定类名指定需要检测的类 |
annotation |
Class<? extends Annotation>[] |
检测指定注解类型是否存在于类路径中 |
value 与 name |
--- | 通常二选一,不要同时指定 |
其中,实际开发最常用的是 value 和 name。
写法一:通过 Class 对象指定类
java
@Configuration
@ConditionalOnClass({Feign.class})
public class FeignConfig {
}
写法二:通过全限定类名指定类
java
@Configuration
@ConditionalOnClass(
name = "feign.Feign"
)
public class FeignConfig {
}
两种写法都可以表达检测 feign.Feign 是否存在的意图。
name 的优势在于:可以使用字符串指定类名,而不需要在当前源码中直接引用这个类。对于可能缺少可选依赖的自动配置场景,这种写法更容易避免类加载方面的问题。
2.3 典型使用场景
假设我们开发一个公共 Starter,其中集成了 Redis。
但是,部分项目没有引入 Redis 相关依赖。
可以这样定义配置:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(
name = "org.springframework.data.redis.core.RedisTemplate"
)
public class RedisAutoConfig {
@Bean
public MyRedisService myRedisService() {
return new MyRedisService();
}
}
当 RedisTemplate 存在时,才启用这份配置。
需要注意:这里的条件只负责判断指定类是否存在,并不代表 Redis 服务已经启动,也不代表 Redis 连接一定成功。
2.4 可以用在什么地方?
@ConditionalOnClass 常见于:
- Spring Boot 自动配置类。
- 公司内部公共 Starter。
- 可选的第三方组件集成。
- 需要兼容不同依赖组合的公共模块。
例如,一个 Starter 可以同时支持 Redis、Kafka、Feign 等组件,但每个业务项目只引入自己需要的依赖。通过条件装配,可以避免把所有组件都强制要求每个项目使用。
三、@ConditionalOnBean:指定 Bean 存在时才装配
3.1 注解的作用
@ConditionalOnBean 用于判断 Spring 容器中是否存在指定的 Bean。
与 @ConditionalOnClass 不同,它检查的不是类路径中有没有某个类,而是 Spring 容器中是否有对应的 Bean 定义或实例。
例如:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MyDataSource.class)
public class DataServiceConfig {
@Bean
public MyDataService myDataService() {
return new MyDataService();
}
}
这里的含义是:
只有当 Spring 容器中存在符合条件的 MyDataSource Bean 时,myDataService 才会参与注册。
注意,MyDataSource.class 存在于项目中,并不意味着 Spring 容器中一定存在 MyDataSource Bean。
例如:
java
public class MyDataSource {
}
仅仅定义这个类,并不会自动创建 Bean。还需要通过 @Component、@Bean 或其他注册机制将其交给 Spring 管理。
3.2 注解有哪些属性?
| 属性 | 类型 | 作用 |
|---|---|---|
value |
Class<?>[] |
指定需要检测的 Bean 类型 |
type |
String[] |
通过类型全限定名指定 Bean 类型 |
name |
String[] |
指定 Bean 名称 |
annotation |
Class<? extends Annotation>[] |
检测是否存在带有指定注解的 Bean |
search |
SearchStrategy |
指定查找 Bean 时是否检查父容器 |
其中,SearchStrategy 常见取值包括:
CURRENT:只检查当前容器。ANCESTORS:检查当前容器及祖先容器。ALL:检查当前容器及祖先容器,效果上相当于同时考虑当前容器和父容器。
通常情况下,使用默认的搜索策略即可。
3.3 示例一:根据 Bean 类型判断
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MyDataSource.class)
public class DataServiceConfig {
@Bean
public MyDataService myDataService() {
return new MyDataService();
}
}
如果容器中有 MyDataSource,就允许注册 MyDataService。
如果没有,则跳过相关配置。
3.4 示例二:根据 Bean 名称判断
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(name = "myDataSource")
public class DataServiceConfig {
@Bean
public MyDataService myDataService() {
return new MyDataService();
}
}
这种写法适用于需要明确依赖某个特定名称 Bean 的情况。
例如:
java
@Bean("myDataSource")
public MyDataSource myDataSource() {
return new MyDataSource();
}
3.5 典型使用场景
假设一个系统中有统一的消息发送组件:
java
public interface MessageSender {
void send(String message);
}
只有在容器中存在消息发送器时,才需要注册消息业务服务:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MessageSender.class)
public class MessageServiceConfig {
@Bean
public MessageService messageService(
MessageSender messageSender) {
return new MessageService(messageSender);
}
}
这样,消息发送器就成为消息业务服务的前置条件。
如果项目没有配置消息发送器,MessageService 就不会因为这份配置而被创建。
3.6 一个非常重要的注意事项
@ConditionalOnBean 对配置处理顺序比较敏感。
假设配置 A 提供了 MyDataSource,配置 B 依赖它:
java
@Configuration
public class ConfigA {
@Bean
public MyDataSource myDataSource() {
return new MyDataSource();
}
}
@Configuration
@ConditionalOnBean(MyDataSource.class)
public class ConfigB {
@Bean
public MyDataService myDataService() {
return new MyDataService();
}
}
如果配置 B 被处理时,Spring Boot 还没有发现配置 A 中符合条件的 Bean 定义,那么 B 的条件可能无法按预期满足。
尤其是在自动配置中,条件判断会受到自动配置发现与处理顺序的影响。
因此,不要把 **@ConditionalOnBean** 当作一个可以随时查询整个应用最终 Bean 状态的动态开关 。如果确实存在自动配置之间的依赖关系,可以结合 @AutoConfigureAfter、@AutoConfigureBefore 等机制明确顺序,并通过集成测试验证。
这些顺序注解属于自动配置排序机制,不是所有普通配置类都需要使用。
四、@ConditionalOnProperty:配置属性满足条件时才装配
4.1 注解的作用
@ConditionalOnProperty 用于根据 Spring Environment 中的配置属性,决定某个配置类或 Bean 是否生效。
这是开发中非常常见的功能开关方式。
例如,我们希望通过配置文件决定是否启用某个功能。
application.yml:
yaml
myapp:
feature:
enabled: true
对应配置类:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
prefix = "myapp.feature",
name = "enabled",
havingValue = "true"
)
public class FeatureConfig {
@Bean
public FeatureService featureService() {
return new FeatureService();
}
}
当配置属性 myapp.feature.enabled 的值为 true 时,条件满足,相关配置才会生效。
如果将其修改为 false,该配置就不会生效。
4.2 注解有哪些属性?
| 属性 | 类型 | 作用 |
|---|---|---|
prefix |
String |
指定属性名前缀 |
name |
String[] |
指定要检测的属性名 |
value |
String[] |
name 的别名 |
havingValue |
String |
指定属性必须匹配的值 |
matchIfMissing |
boolean |
属性不存在时是否仍然视为匹配 |
其中,name 是最重要的属性之一。
例如:
java
@ConditionalOnProperty(
prefix = "myapp.feature",
name = "enabled",
havingValue = "true",
matchIfMissing = false
)
表示检查 myapp.feature.enabled 是否等于字符串 "true"。
4.3 havingValue 有什么用?
这是一个容易混淆的属性。
假设配置如下:
yaml
myapp:
feature:
enabled: false
情况一:不指定 **havingValue**
java
@ConditionalOnProperty(
prefix = "myapp.feature",
name = "enabled"
)
默认情况下,只要属性存在,并且其值不等于 "false"(忽略大小写),条件就会匹配。
因此:
| 配置值 | 默认规则是否匹配 |
|---|---|
true |
是 |
false |
否 |
abc |
是 |
| 属性不存在 | 默认否 |
情况二:指定 **havingValue = "true"**
java
@ConditionalOnProperty(
prefix = "myapp.feature",
name = "enabled",
havingValue = "true"
)
此时只有属性值与 "true" 匹配时,条件才满足。
例如:
| 配置值 | 是否匹配 |
|---|---|
true |
是 |
false |
否 |
abc |
否 |
| 属性不存在 | 默认否 |
实际开发中,如果这个属性代表一个布尔开关,建议明确写出 havingValue = "true",这样代码意图更清晰。
4.4 matchIfMissing 有什么用?
这个属性决定:当配置项不存在时,是否仍然允许配置生效。
例如:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
prefix = "myapp.feature",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
public class FeatureConfig {
}
如果没有配置 myapp.feature.enabled,条件仍然会匹配。
需要注意,matchIfMissing = true 并不是忽略所有配置判断,而是只对属性缺失的情况改变处理方式。如果属性明确设置为 false,条件依然不满足。
对于功能开关,需要根据业务风险决定默认行为:
- 功能默认关闭:通常使用
matchIfMissing = false。 - 功能默认开启:可以考虑
matchIfMissing = true,但要评估默认开启带来的影响。
4.5 实际业务示例:控制定时任务是否启用
假设有一个对账定时任务,不同环境可能有不同的运行要求。
application.yml:
yaml
car:
reconcile:
enabled: false
配置类:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
prefix = "car.reconcile",
name = "enabled",
havingValue = "true"
)
public class ReconcileConfig {
@Bean
public ReconcileService reconcileService() {
return new ReconcileService();
}
}
这样就可以通过配置文件控制对账服务是否参与 Bean 注册。
但是,如果使用的是 Quartz、@Scheduled 或其他独立的任务注册机制,还需要确认任务调度器本身是否仍会注册任务,以及任务方法是否还可能通过其他路径执行。
条件装配控制的是相应配置或 Bean 的注册,并不等于自动取消系统中所有与该功能有关的行为。
4.6 一个使用限制:集合属性
@ConditionalOnProperty 不适合可靠地检测集合属性是否存在。
例如:
yaml
myapp:
servers:
- server1
- server2
如果希望根据 myapp.servers 这类集合配置判断条件,@ConditionalOnProperty 可能无法得到预期结果。
对于复杂配置,通常可以考虑使用 @ConfigurationProperties 绑定属性,再结合自定义 Condition 实现更精确的判断。
五、@ConditionalOnMissingBean:没有指定 Bean 时才装配
5.1 注解的作用
@ConditionalOnMissingBean 用于判断 Spring 容器中是否不存在指定的 Bean。
如果不存在,则允许对应配置或 Bean 定义生效;如果已经存在,则跳过这份默认配置。
它在 Spring Boot 自动配置中非常重要。
例如:
java
@Configuration(proxyBeanMethods = false)
public class MyAutoConfig {
@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService() {
return new MyService();
}
}
这段代码的含义是:
- 如果容器中还没有符合条件的
MyServiceBean,就注册默认实现。 - 如果用户已经提供了
MyServiceBean,就不再通过这个方法创建默认实现。
这体现了 Spring Boot 的一个重要设计理念:
框架提供默认实现,但允许开发者根据业务需求进行覆盖。
5.2 注解有哪些属性?
| 属性 | 类型 | 作用 |
|---|---|---|
value |
Class<?>[] |
指定要检查的 Bean 类型 |
type |
String[] |
通过全限定类名指定 Bean 类型 |
name |
String[] |
指定要检查的 Bean 名称 |
annotation |
Class<? extends Annotation>[] |
根据 Bean 所带的注解进行判断 |
ignored |
Class<?>[] |
指定在缺失 Bean 判断中忽略的类型 |
ignoredType |
String[] |
通过类名指定需要忽略的类型 |
search |
SearchStrategy |
指定检查 Bean 时的容器搜索范围 |
这些属性与 @ConditionalOnBean 有不少相似之处,但两者的判断方向正好相反。
5.3 示例一:提供默认服务实现
java
public interface GreetingService {
String greet();
}
默认实现:
java
public class DefaultGreetingService implements GreetingService {
@Override
public String greet() {
return "Hello, Spring Boot!";
}
}
自动配置类:
java
@Configuration(proxyBeanMethods = false)
public class GreetingAutoConfig {
@Bean
@ConditionalOnMissingBean(GreetingService.class)
public GreetingService greetingService() {
return new DefaultGreetingService();
}
}
如果用户没有自定义 GreetingService,Spring Boot 就会提供默认实现。
5.4 示例二:用户自定义 Bean 覆盖默认实现
用户可以自行定义:
java
@Configuration(proxyBeanMethods = false)
public class CustomGreetingConfig {
@Bean
public GreetingService greetingService() {
return new GreetingService() {
@Override
public String greet() {
return "Hello, custom service!";
}
};
}
}
当用户的 Bean 定义能够被条件判断识别时,自动配置中的默认 GreetingService 就不会再被注册。
最终,业务代码可以直接注入 GreetingService,不需要关心当前使用的是默认实现还是自定义实现。
5.5 为什么 Spring Boot 自动配置经常使用它?
假设我们开发一个公共 Starter,为所有业务系统提供默认的线程池。
java
@Configuration(proxyBeanMethods = false)
public class ThreadPoolAutoConfig {
@Bean
@ConditionalOnMissingBean(name = "businessExecutor")
public Executor businessExecutor() {
return Executors.newFixedThreadPool(10);
}
}
如果业务项目没有自己定义名为 businessExecutor 的 Bean,就使用默认线程池。
如果业务项目已经提供了同名 Bean,就让用户的定义优先生效。
实际项目中,线程池通常还需要考虑线程命名、拒绝策略、队列容量、优雅关闭等问题。这里重点是说明条件装配的工作方式。
5.6 为什么它经常写在 @Bean 方法上?
条件注解既可以作用于配置类,也可以作用于 @Bean 方法。
例如:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(MyService.class)
public class MyServiceAutoConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyOtherService myOtherService() {
return new MyOtherService();
}
}
这里条件加在类上,意味着整个配置类都受到条件控制。
如果条件不满足,类中的两个 Bean 方法都不会通过这份配置注册 Bean。
而如果写在单个方法上:
java
@Configuration(proxyBeanMethods = false)
public class MyServiceAutoConfig {
@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService() {
return new MyService();
}
@Bean
public MyOtherService myOtherService() {
return new MyOtherService();
}
}
则只有 myService() 对应的 Bean 定义受到这个条件约束,myOtherService() 不会因为该方法上的条件而被跳过。
因此,类级别的条件影响整个配置类,方法级别的条件只影响对应的 Bean 定义,这也是四个注解都需要理解的共同规则。
六、四个注解的核心区别
我们可以通过一个例子记住它们。
假设现在要创建一个 Feign 相关服务:
- 项目必须引入 Feign 相关依赖。
- Spring 容器必须已经存在一个身份认证组件。
- 用户必须在配置文件中开启 Feign 服务。
- 如果用户已经提供了自定义服务,就不再创建默认服务。
对应的判断分别是:
java
@ConditionalOnClass(Feign.class)
判断类路径中是否存在 Feign 类。
java
@ConditionalOnBean(AuthService.class)
判断容器中是否存在身份认证服务。
java
@ConditionalOnProperty(
prefix = "myapp.feign",
name = "enabled",
havingValue = "true"
)
判断配置开关是否开启。
java
@ConditionalOnMissingBean(FeignBusinessService.class)
判断容器中是否还没有对应的业务服务。
这四种条件可以组合使用。
例如:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(AuthService.class)
@ConditionalOnProperty(
prefix = "myapp.feign",
name = "enabled",
havingValue = "true"
)
public class FeignBusinessAutoConfig {
@Bean
@ConditionalOnMissingBean(FeignBusinessService.class)
public FeignBusinessService feignBusinessService(AuthService authService) {
return new FeignBusinessService(authService);
}
}
这份配置表达了多重约束:只有 Feign 类存在、认证 Bean 存在、配置开关开启,并且尚未存在符合条件的业务服务时,才会通过该方法注册默认服务。
不过,实际自动配置中还应当考虑 Bean 的发现顺序以及认证组件的来源,不能仅凭注解组合就假定所有依赖一定会被正确识别。
七、这些注解到底作用在哪些地方?
这四个注解的常见使用位置有两类。
7.1 作用在配置类上
java
@Configuration
@ConditionalOnClass(Feign.class)
public class FeignConfig {
}
条件不满足时,整个配置类不会按正常条件匹配结果生效。
适合控制一组相关 Bean 是否整体参与配置。
7.2 作用在 @Bean 方法上
java
@Configuration
public class MyConfig {
@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService() {
return new MyService();
}
}
条件只影响这个方法对应的 Bean 定义。
适合对某一个默认 Bean 做精确控制。
需要注意,这些注解的目标是配置处理与 Bean 注册,并不是在每次调用 Java 方法时重新检查条件。它们也不会自动替代业务代码中的权限校验、参数验证或运行时状态判断。
八、实际开发中容易遇到的几个问题
8.1 为什么 @ConditionalOnClass 能检测到类,却仍然启动失败?
因为类存在只是一个条件,不代表该类的所有依赖都正确。
例如,项目可能引入了 Feign 类,但相关自动配置、其他依赖或者连接配置不完整,依然可能启动失败。
此外,@ConditionalOnClass 不会检查远程服务是否可用。
8.2 为什么 @ConditionalOnBean 没有按预期生效?
常见原因包括:
- 对应 Bean 没有注册。
- Bean 类型与实际条件不匹配。
- 自动配置之间的处理顺序不符合预期。
- Bean 位于父容器或子容器中,而搜索策略没有覆盖目标容器。
排查时可以查看启动日志、条件评估报告,以及容器中实际注册的 Bean。
8.3 为什么 @ConditionalOnProperty 不生效?
重点检查:
- 属性名和前缀是否正确。
- 配置文件是否被当前环境加载。
- 是否存在其他配置来源覆盖了该属性。
havingValue是否与实际属性值匹配。- 属性缺失时的
matchIfMissing是否符合预期。
例如:
yaml
myapp:
feature:
enabled: true
对应的属性名是:
tex
myapp.feature.enabled
前缀和属性名必须组合正确。
8.4 为什么 @ConditionalOnMissingBean 仍然创建了默认 Bean?
需要检查:
- 用户自定义 Bean 是否真的注册进容器。
- 自定义 Bean 的类型是否匹配条件。
- 是否检查了正确的 Bean 名称。
- 自动配置处理顺序是否符合预期。
尤其在 Spring Boot 自动配置中,建议优先让业务自定义配置正常参与容器配置,再通过自动配置条件提供默认实现。
九、如何查看 Spring Boot 条件装配结果?
在 Spring Boot 应用中,如果需要排查为什么某个自动配置生效或未生效,可以开启调试模式。
在 application.yml 中:
yaml
debug: true
或者启动应用时添加:
tex
java -jar app.jar --debug
在支持条件评估报告的 Spring Boot 版本中,可以通过启动日志中的 Condition Evaluation Report 查看自动配置条件的匹配情况。
报告通常会区分:
- Positive matches:匹配成功的条件。
- Negative matches:匹配失败的条件。
- Exclusions:被排除的自动配置。
- Unconditional classes:不受普通匹配条件控制的相关配置。
具体输出内容会受到 Spring Boot 版本与配置方式影响。
通过这些信息,可以更容易定位某个自动配置为什么没有生效。
十、总结
Spring Boot 条件装配机制的目的,是让配置能够根据项目依赖、容器状态和外部配置灵活地启用或跳过。
四个常用注解可以这样记忆:
@ConditionalOnClass:类存在,才考虑装配。@ConditionalOnBean:指定 Bean 存在,才考虑装配。@ConditionalOnProperty:配置属性满足条件,才考虑装配。@ConditionalOnMissingBean:指定 Bean 不存在,才提供默认实现。
其中,@ConditionalOnClass 和 @ConditionalOnBean 分别关注类路径与 Spring 容器;@ConditionalOnProperty 关注配置属性;@ConditionalOnMissingBean 则是 Spring Boot 默认实现可被覆盖的重要基础。
在实际开发中,尤其是编写公共组件、Starter、Feign 配置、Redis 配置和业务功能开关时,合理使用这些注解,可以减少不必要的 Bean 注册,降低组件之间的耦合,并提高项目配置的灵活性。
需要记住的是:条件装配决定的是配置或 Bean 定义是否参与注册,不是对整个业务功能运行状态的全面保证。