Spring Boot 条件装配注解详解

目录

    • [一、什么是 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();
    }
}

这段代码的含义是:

  • 如果容器中还没有符合条件的 MyService Bean,就注册默认实现。
  • 如果用户已经提供了 MyService Bean,就不再通过这个方法创建默认实现。

这体现了 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 相关服务:

  1. 项目必须引入 Feign 相关依赖。
  2. Spring 容器必须已经存在一个身份认证组件。
  3. 用户必须在配置文件中开启 Feign 服务。
  4. 如果用户已经提供了自定义服务,就不再创建默认服务。

对应的判断分别是:

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 定义是否参与注册,不是对整个业务功能运行状态的全面保证。

相关推荐
秦时明月之君临天下1 小时前
MySQL purge 参数详解:innodb_purge_threads 与 innodb_purge_batch_size
数据库·mysql·1024程序员节
云和数据.ChenGuang1 小时前
python sorted函数详细剖析
开发语言·人工智能·windows·python·1024程序员节
木井巳1 小时前
【动态规划: 路径问题】不同路径
java·算法·leetcode·动态规划
qq_355939491 小时前
标签打印发虚、断线、糊成一团?先分清是哪种“不清“
1024程序员节
打工仔折腾 AI1 小时前
AI Agent 工程化实战(二):5 分钟跑通 Deep Agent 并逐层拆解底层链路
java·数据库·人工智能·后端·python·langchain·ai agent 实战
云和数据.ChenGuang1 小时前
Python map() 函数详解:从语法、原理到实战用法
开发语言·人工智能·python·langchain·1024程序员节
秋名RG1 小时前
(二)存储、CPU 与指令执行——程序为什么快,又为什么慢
java·开发语言
BeiGuTing_code2 小时前
Java泛型与Object
java·开发语言
惊讶的猫2 小时前
Tika文档解析
java