前言
@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放在一个配置类里面注入。