• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Spring Boot 基础篇–Bean(5):@ConditionalOnBean与@ConditionalOnClass

Spring Boot winrains 来源:一灰灰Blog 12个月前 (11-08) 65次浏览

bean的条件注入,除了前面一篇博文中介绍的通过@Conditional注解配合Condition接口的实现之外,还提供了更多简化的注解使用方式,省略了自己实现Condtion接口,本篇博文主要介绍下面几个常用的注解使用方式

  • @ConditionalOnBean
  • @ConditionalOnMissingBean
  • @ConditionalOnClass
  • @ConditionalOnMissingClass

I. Bean的存在与否作为条件

当Bean不存在时,创建一个默认的Bean,在Spring的生态中可以说比较常见了;接下来看下这种方式可以怎么用

1. @ConditionalOnBean

要求bean存在时,才会创建这个bean;如我提供了一个bean名为RedisOperBean,用于封装redis相关的操作;但是我这个bean需要依赖restTemplate这个bean,只有当应用引入了redis的相关依赖,并存在RestTemplate这个bean的时候,我这个bean才会生效
假设bean的定义如下

@Component
@ConditionalOnBean(name = "redisTemplate")
public class RedisOperBean {
    private final RedisTemplate redisTemplate;
    public RedisOperBean(RedisTemplate redisTemplate) {
        // ...
    }
}

这样的好处就是我提供的这个第三方包,如果被用户A间接依赖(但是A本身不需要操作redis),也不会因为创建RedisOperBean而抛异常

产生异常的原因是因为找不到RestTemplate的bean,因此无法实例化RedisOperBean,从而抛出异常

a. 注解定义

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
    // bean类型
    Class<?>[] value() default {};
    // bean类型
    String[] type() default {};
    // 要求bean上拥有指定的注解
    Class<? extends Annotation>[] annotation() default {};
    // bean names
    String[] name() default {};
    SearchStrategy search() default SearchStrategy.ALL;
}

b. 测试用例

构建一个简单的测试用例,先定义一个基础的bean

public class DependedBean {
}

再定义一个依赖只有上面的bean存在时,才会加载的bean

public class LoadIfBeanExist {
    private String name;
    public LoadIfBeanExist(String name) {
        this.name = name;
    }
    public String getName() {
        return "load if bean exists: " + name;
    }
}

接下来就是bean的定义了

@Bean
public DependedBean dependedBean() {
    return new DependedBean();
}
/**
 * 只有当DependedBean 存在时,才会创建bean: `LoadIfBeanExist`
 *
 * @return
 */
@Bean
@ConditionalOnBean(name = "dependedBean")
public LoadIfBeanExist loadIfBeanExist() {
    return new LoadIfBeanExist("dependedBean");
}

根据上面的测试用例,LoadIfBeanExist是会被正常加载的; 具体结果看后面的实例演示

2. ConditionalOnMissingBean

和前面一个作用正好相反的,上面是要求存在bean,而这个是要求不存在

a. 接口定义

public @interface ConditionalOnMissingBean {
    Class<?>[] value() default {};
    String[] type() default {};
    /**
     * The class type of beans that should be ignored when identifying matching
     * beans.
     */
    Class<?>[] ignored() default {};
    /**
     * The class type names of beans that should be ignored when identifying
     * matching beans.
     */
    String[] ignoredType() default {};
    Class<? extends Annotation>[] annotation() default {};
    String[] name() default {};
    SearchStrategy search() default SearchStrategy.ALL;
}

b. 测试用例

同样定义一个bean不存在时,才创建的bean

public class LoadIfBeanNotExists {
    public String name;
    public LoadIfBeanNotExists(String name) {
        this.name = name;
    }
    public String getName() {
        return "load if bean not exists: " + name;
    }
}

对应的bean配置如下

/**
 * 只有当没有notExistsBean时,才会创建bean: `LoadIfBeanNotExists`
 *
 * @return
 */
@Bean
@ConditionalOnMissingBean(name = "notExistsBean")
public LoadIfBeanNotExists loadIfBeanNotExists() {
    return new LoadIfBeanNotExists("notExistsBean");
}

因为没有notExistsBean,所以上面这个bean也应该被正常注册

3. 实例演示

因为bean的是否存在和class的是否存在有较大的相似性,因此实例演示放在下一小节,一起测试

II. Class的存在与否作为条件

从使用来看,和前面基本上没有太大的区别,无非就是将bean换成了class;这样就可以避免因为Class Not Found导致的编译异常了

1. @ConditionalOnClass

要求class存在

a. 注解定义

public @interface ConditionalOnClass {
    Class<?>[] value() default {};
    /**
     * The classes names that must be present.
     * @return the class names that must be present.
     */
    String[] name() default {};
}

b. 测试用例

先定义一个class

public class DependedClz {
}

然后依赖class存在的bean

public class LoadIfClzExists {
    private String name;
    public LoadIfClzExists(String name) {
        this.name = name;
    }
    public String getName() {
        return "load if exists clz: " + name;
    }
}

接下来就是Bean的配置

/**
 * 当引用了 {@link DependedClz} 类之后,才会创建bean: `LoadIfClzExists`
 *
 * @return
 */
@Bean
@ConditionalOnClass(DependedClz.class)
public LoadIfClzExists loadIfClzExists() {
    return new LoadIfClzExists("dependedClz");
}

因为类存在,所以测试时,这个bean应该被正常注册

2. @ConditionalOnMissingClass

class不存在时,才会加载bean

a. 注解定义

public @interface ConditionalOnMissingClass {
    String[] value() default {};
}

b. 测试用例

定义一个class缺少时才会创建的bean

public class LoadIfClzNotExists {
    private String name;
    public LoadIfClzNotExists(String name) {
        this.name = name;
    }
    public String getName() {
        return "load if not exists clz: " + name;
    }
}

bean的配置如下

/**
 * 当系统中没有 com.git.hui.boot.conditionbean.example.depends.clz.DependedClz类时,才会创建这个bean
 *
 * @return
 */
@Bean
@ConditionalOnMissingClass("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz")
public LoadIfClzNotExists loadIfClzNotExists() {
    return new LoadIfClzNotExists("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz");
}

因为上面这个类存在,所以这个bean不应该被正常注册

3. 实例演示

起一个rest服务,测试下上面的四个bean是否正常

@RestController
@RequestMapping("depends")
public class DependRest {
    @Autowired
    private LoadIfBeanExist loadIfBeanExist;
    @Autowired
    private LoadIfBeanNotExists loadIfBeanNotExists;
    @Autowired
    private LoadIfClzExists loadIfClzExists;
    @Autowired(required = false)
    private LoadIfClzNotExists loadIfClzNotExists;
    @GetMapping(path = "show")
    public String show() {
        Map<String, String> result = new HashMap<>(4);
        // 存在
        result.put("loadIfBeanExist", loadIfBeanExist == null ? "null ==> false!" : loadIfBeanExist.getName());
        // 存在
        result.put("loadIfBeanNotExists",
                loadIfBeanNotExists == null ? "null ==> false!" : loadIfBeanNotExists.getName());
        // 存在
        result.put("loadIfClzExists", loadIfClzExists == null ? "null ==> false!" : loadIfClzExists.getName());
        // 不存在
        result.put("loadIfClzNotExists", loadIfClzNotExists == null ? "null ==> true!" : loadIfClzNotExists.getName());
        return JSONObject.toJSONString(result);
    }
}

根据前面的分析,返回的结果应该是三个存在,一个不存在;下图执行和我们预期一致
条件依赖注册演示条件依赖注册演示

作者:一灰灰Blog

来源:http://spring.hhui.top/spring-blog/2018/10/19/181019-SpringBoot%E5%9F%BA%E7%A1%80%E7%AF%87Bean%E4%B9%8B-ConditionalOnBean%E4%B8%8E@ConditionalOnClass/


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (0)