使用了 @ConditionalOnClass 却仍然报找不到类的错

本文通过作者在使用 Spring Boot 的 @ConditionalOnClass 注解时遇到的错误经历,详细分析了当条件类缺失时,由于 @Bean 方法的依赖导致的运行时异常。总结出在使用 @ConditionalOnClass 时,需要注意返回值类与条件类的关系,以避免类似问题的发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

@ConditionalOnClass的作用是当有该类时,才符合条件,继续执行其他逻辑。

@ConditionalOnClass包含两个属性:value和name

value是class数组,name是string数组。当一个类不确定是否在classpath当中时,建议使用name属性,就算找不到该类时,也不会报错。

我的错误经历

概况就是,我使用了一个配置类,注入了两个bean,分别是HealthIndicator和Listener2。

比较特殊的是,HealthIndicator继承了AbstractHealthIndicator,所以需要有AbstractHealthIndicator存在,才能创建HealthIndicator的bean。

public class HealthIndicator extends AbstractHealthIndicator {

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        System.out.println("test");
    }
}

public class Listener2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(Listener2.class);

    public void hit(String cacheName, String key) {}

    public void miss(String cacheName, String key) {}
}

@Configuration
public class Listener2Configuration {

    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.health.AbstractHealthIndicator")
    public HealthIndicator healthIndicator() {
        return new HealthIndicator();
    }

    @Bean
    @ConditionalOnMissingBean
    public Listener2 listener2() {
        return new Listener2();
    }

}

原以为这样,可以达到自己想要的结果。可是,一运行项目就报错。如下:

Caused by: java.lang.ClassNotFoundException: org.springframework.boot.actuate.health.AbstractHealthIndicator

at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_171]

at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_171]

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_171]

at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_171]

... 37 common frames omitted

原因是:没有AbstractHealthIndicator这个类。

可是明明有加@ConditionalOnClass(name=“org.springframework.boot.actuate.health.AbstractHealthIndicator”)

依旧报错。。。

分析

debug整个项目的启动过程,发现在加载Listener2Configuration时,正如期望一样,healthIndicator不符合条件,故而跳过,不会有healthIndicator的beandefinition存在。
但是,后续环境中可能存在一些含有@ConditionalOnMissingBean的beanDefinition。在加载他们时,会去找容器中有没有该bean,结果就需要先更新下容器的bean,而之前幸存的listener2,引发spring去获取Listener2Configuration的所有方法,也就包含寻找healthIndicator()。而该方法的返回值类 HealthIndicator 无法加载成功,因为缺少了AbstractHealthIndicator,所以此时抛出异常,找不到类。

总结经验

@ConditionalOnClass用在类上面是绝对可靠的。但只要在具有@Bean的方法上使用@ConditionalOnClass时,要注意返回值类是否与条件类相关。如果是的话,如上有继承关系,最好还是有一个专门注入该bean(如上述的healthIndicator)的类,而不是混杂着其他bean放在一个配置类里面注入。

<think>嗯,用户问的是“SpringBoot车祸现场是指什么”,我需要先理解这个问题的背景。首先,“车祸现场”在中文网络用语中通常用来比喻某个事情出现了严重的问题或混乱的情况,带有调侃或自嘲的意味。所以用户可能是在问SpringBoot开发中常见的误或者容易出问题的地方。 接下来,我需要回忆一下SpringBoot相关的常见问题。SpringBoot虽然简化了配置,但开发者在使用时还是会遇到各种坑。比如自动配置的冲突,版本依赖的问题,配置文件的误,Bean的注入问题,事务管理不当,缓存配置误等等。这些都是可能导致应用崩溃或者运行异常的情况,符合“车祸现场”的描述。 然后,我需要考虑用户可能的背景。用户可能是刚接触SpringBoot的开发者,遇到了问题,或者在社区里看到相关术语,想了解具体指哪些情况。因此,回答需要结构清晰,分点说明,每个问题点给出例子和解决方法,帮助用户避免这些误。 另外,用户提供的系统级指令要求使用中文回答,数学公式用$...$格式,独立公式用$$...$$,但这个问题可能不涉及数学公式。不过需要注意回答的结构,确保每个部分明确,比如分点列出常见的“车祸现场”场景,每个场景下详细说明原因和解决方案。 还需要验证信息的准确性,比如自动配置的原理,版本依赖如何解决,配置文件的优先级,Bean的作用域等,确保回答内容正确可靠。同时,语言要口语化,避免技术术语过于晦涩,让新手也能理解。 最后,总结部分需要强调这些问题是开发中的常见陷阱,通过经验积累和最佳实践可以避免,这样用户能有一个正面的收获,知道如何应对这些问题。</think>SpringBoot "车祸现场" 是开发者社群中常用的调侃说法,主要指在 SpringBoot 开发过程中因配置不当、技术理解偏差或常见误导致的严重问题或混乱场景。以下是典型场景及技术解析: --- ### 1. **自动配置冲突** **场景**: 同时引入`spring-boot-starter-data-jpa`和`spring-boot-starter-data-mongodb`未指定主数据源,启动时`No qualifying bean of type 'javax.sql.DataSource'`。 **原理**: SpringBoot 的`@ConditionalOnClass`注解触发多个数据源自动配置,但未通过`@Primary`标记主 Bean。 **解决方案**: ```java @Configuration public class DataSourceConfig { @Bean @Primary @ConfigurationProperties(prefix="spring.datasource") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } } ``` --- ### 2. **版本依赖雪崩** **场景**: 引入第三方库如`xxl-job-core 2.3.0`导致 SpringBoot 2.7.x 项目启动失败,`MethodNotFoundException`。 **根本原因**: 库内部依赖的 Spring 版本与项目主版本不兼容,通过 Maven 依赖树可验证: ```bash mvn dependency:tree -Dincludes=org.springframework ``` **解决步骤**: 1. 显式声明兼容版本依赖 2. 使用`<exclusions>`排除冲突依赖 3. 或升级第三方库到适配版本 --- ### 3. **配置文件优先级陷阱** **误现象**: 同时存在`application.yml`和`bootstrap.yml`时,Nacos 配置中心加载失败。 **优先级规则**: SpringBoot 配置加载顺序为: 1. `bootstrap.properties` (高) 2. `application.properties` 3. `bootstrap.yml` 4. `application.yml` (低) **正确实践**: 分布式配置应写在`bootstrap.yml`中,且需确保引入`spring-cloud-starter-bootstrap`依赖。 --- ### 4. **循环依赖死锁** **典型**: `The dependencies of some of the beans in the application context form a cycle` **代码示例**: ```java @Service class AService { @Autowired BService b; // A依赖B } @Service class BService { @Autowired AService a; // B反向依赖A } ``` **解决方案**: - 使用`@Lazy`延迟加载 - 重构代码结构,引入 DTO 解耦 - SpringBoot 2.6+ 默认禁止循环依赖,可通过`spring.main.allow-circular-references=true`临时绕过(不推荐) --- ### 5. **事务传播误解** **隐蔽 Bug**: 在`@Transactional`方法内调用同其他方法导致事务失效。 **技术原理**: Spring 事务基于 AOP 代理实现,自调用会绕过代理机制。 **修正方案**: ```java @Service public class OrderService { public void outerMethod() { innerMethod(); // 误:直接调用不走代理 ((OrderService)AopContext.currentProxy()).innerMethod(); // 正确:通过代理调用 } @Transactional public void innerMethod() { // 数据库操作 } } ``` --- ### 6. **Actuator 信息泄露** **安全漏洞**: 未配置`management.endpoints.web.exposure.include`时,默认暴露`/actuator/health`以外的端点。 **风险案例**: 攻击者访问`/actuator/env`可获取数据库密码等敏感信息。 **加固方案**: ```yaml management: endpoints: web: exposure: include: health,info endpoint: env: enabled: false beans: enabled: false ``` --- ### 经验总结 | 问题型 | 预防措施 | 调试工具 | |---------|---------|---------| | 配置冲突 | 使用`spring-boot-autoconfigure-processor` | `/actuator/conditions` | | 依赖问题 | 定期执行`mvn dependency:analyze` | Maven Enforcer 插件 | | 事务异常 | 开启调试日志`logging.level.org.springframework.jdbc=DEBUG` | JDBC 日志分析工具 | 通过合理使用 SpringBoot 提供的`/actuator`端点、结合 APM 工具(如 SkyWalking)进行运行时监控,可显著降低"车祸"发生概率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值