背景

假如现在有个jar包叫做A.jar,我想根据引用这个A.jar的项目是否有/无某些类,来决定加载是否加载A.jar的Bean
我们就可以考虑@ConditionalOnClass或者ConditionalOnMissingClass

所以我们的代码会写成

  1. @Configuration
  2. public class AirOpenAutoConfiguration {
  3. @ConditionalOnClass({CasHelper.class})
  4. @ConditionalOnMissingBean(name = "airOpenAuthenticationFilter")
  5. @Configuration
  6. public class OpenCasAuthentication {
  7. @Bean("airOpenAuthenticationFilter")
  8. public FilterRegistrationBean<AirOpenAuthenticationFilter> airOpenAuthenticationFilterRegistration() {
  9. FilterRegistrationBean<AirOpenAuthenticationFilter> filter = new FilterRegistrationBean<>();
  10. return filter;
  11. }
  12. }

要点1:加入true

这里我们判断是否有CasHelper.class这个类,有的话就创建这个bean,这个CasHelper类是在另外一个jar包里面的。所以我们需要引入了这个,但是只能是是A.jar引用,不能传递到下去。所以需要引入的时候改成这样

  1. <dependency>
  2. <groupId>com.xxx</groupId>
  3. <artifactId>cas</artifactId>
  4. <scope>compile</scope>
  5. <optional>true</optional>
  6. </dependency>

这里一定要带上<optional>true</optional>,这样依赖就不会传递下去。只保证A.jar里面是有这个CasHelper,比如有个项目B引入了这个A.jar,假如B没有引入cas这个包,那么这个CasHelper将会不存在与B的项目中,这时
@ConditionalOnClass({CasHelper.class})会判定CasHelper不存在,然后不加载这个airOpenAuthenticationFilter的Bean。

要点2:@ConditionalOnClass需要配合@Configuration使用,不与@Bean配合使用

假如我们是这样写的

  1. @Configuration
  2. public class AirOpenAutoConfiguration {
  3. @ConditionalOnClass({CasHelper.class})
  4. @ConditionalOnMissingBean(name = "airOpenAuthenticationFilter")
  5. @Bean("airOpenAuthenticationFilter")
  6. public FilterRegistrationBean<AirOpenAuthenticationFilter> airOpenAuthenticationFilterRegistration() {
  7. FilterRegistrationBean<AirOpenAuthenticationFilter> filter = new FilterRegistrationBean<>();
  8. return filter;
  9. }

也就是将注解放在方法上面,并且和@Bean一起使用,这个时候就会出现这个报错sun.reflect.annotation.TypeNotPresentExceptionProxy

  1. Using the @ConditionalOnClass annotation in the method results in "java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy"

所以在我们使用@ConditionalOnXXX相关的注解时候,最好是用对这个bean再封装一层@Configuration,然后再这个Configuration上面去使用@ConditionalOnXXX,这个也是官方的建议,所以我们需要遵循这个来。

类似以下:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass({ JobLauncher.class, DataSource.class })
  3. @AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
  4. @ConditionalOnBean(JobLauncher.class)
  5. @EnableConfigurationProperties(BatchProperties.class)
  6. @Import(BatchConfigurerConfiguration.class)
  7. public class BatchAutoConfiguration {
  8. @Bean
  9. @ConditionalOnMissingBean(ExitCodeGenerator.class)
  10. public JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() {
  11. return new JobExecutionExitCodeGenerator();
  12. }
  13. @Configuration(proxyBeanMethods = false)
  14. @ConditionalOnBean(DataSource.class)
  15. @ConditionalOnClass(DatabasePopulator.class)
  16. static class DataSourceInitializerConfiguration {
  17. @Bean
  18. @ConditionalOnMissingBean
  19. BatchDataSourceInitializer batchDataSourceInitializer(DataSource dataSource,
  20. @BatchDataSource ObjectProvider<DataSource> batchDataSource, ResourceLoader resourceLoader,
  21. BatchProperties properties) {
  22. return new BatchDataSourceInitializer(batchDataSource.getIfAvailable(() -> dataSource), resourceLoader,
  23. properties);
  24. }
  25. }
  26. }