Spring Boot 教程

@ConditionalOnBean 注解

在 Spring4 中推出了注解,方便程序根据当前环境或者容器情况来动态注入 bean。在 SpringBoot,SpringCloud 中基于注解扩展了很多更实用的条件注解。本章节将介绍 @ConditionalOnBean 条件注解的作用和用法。

简介

@ConditionalOnBean 注解用来指定多个 bean,只有当指定的所有 bean 均已经包含在 BeanFactory 中时才匹配  @Conditional 注解。但是同一 bean 不必满足这些要求。

当 @ConditionalOnBean 注解放置在 @Bean 方法上时,bean 类默认为工厂方法的返回类型,例如:

@Configuration
public class MyAutoConfiguration {

    @ConditionalOnBean
    @Bean
    public MyService myService() {
        ...
    }

}

在上面的示例中,如果 BeanFactory 中已经包含 MyService 类型的 Bean,则条件将匹配。

该条件只能匹配到目前为止应用程序上下文已处理的 bean 定义。因此,强烈建议仅在自动配置类上使用此条件。如果候选 bean 可能是由另一种自动配置创建的,请确保使用此条件的 bean 在之后运行。源码如下:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {

   /**
    * 当在 BeanFactory 中包含指定的 bean 时,该条件匹配。
    */
   Class<?>[] value() default {};

   /**
    * 应该检查的bean的类类型名称。当BeanFactory中包含指定的所有类的bean时,条件匹配。
    */
   String[] type() default {};

   /**
    * 当在 BeanFactory 中的 bean 上定义了所有指定的注释时,该条件匹配。
    */
   Class<? extends Annotation>[] annotation() default {};

   /**
    * 要检查的bean的名称。当所有指定的Bean名称都包含在BeanFactory中时,条件匹配。
    */
   String[] name() default {};

   /**
    * 决定是否应考虑应用程序上下文层次结构(父上下文)的策略。
    */
   SearchStrategy search() default SearchStrategy.ALL;

   /**
    * 可能在其通用参数内包含指定 Bean 类型的其他类。例如,声明 value = Name.class 和
    * parameterizedContainer = NameRegistration.class 的注解,将同时检测 Name 和 NameRegistration<Name>。
    */
   Class<?>[] parameterizedContainer() default {};

}

实例

我们创建用户和订单服务,然后分别通过两个 @Configuration 来自动配置服务,服务将根据 @ConditionalOnBean 条件动态创建。

(1)创建两个服务

a、UserService.java 服务

public class UserService {

}

b、OrderService.java 服务

public class OrderService {
    
}

(2)创建两个 @Configuration 类,并在类中通过 @ConditionalOnBean 进行条件自动配置。

a、UserConfig.java 配置

@Configuration
@ConditionalOnBean(InitUser.class)
public class UserConfig {

    @Bean
    public UserService userService() {
        System.out.println("UserService -> userService()");
        return new UserService();
    }

}

b、OrderConfig.java 配置

@Configuration
@ConditionalOnBean(InitOrder.class)
public class OrderConfig {

    @Bean
    public OrderService orderService() {
        System.out.println("OrderConfig -> orderService()");
        return new OrderService();
    }

}

(3)上面用户和订单配置中,通过 @ConditionalOnBean 注解要求用户服务必须要在 BeanFactory 中包含 InitUser Bean;要求订单服务必须要在 BeanFactory 中包含 InitOrder Bean。如下图:

// 初始化用户服务
@Component
public class InitUser {

}

// 初始化订单服务
public class InitOrder {

}

上图中,InitUser 使用了 @Component 注解将它注入到 BeanFactory 中;而 InitOrder 并没有使用任何注解,没有被注入到 BeanFactory 中,因此就导致 @ConditionalOnBean(InitOrder.class) 匹配失败,不去自动配置 orderService()。

(4)客户端,在客户端通过 ApplicationContext 类动态获取注入的服务。如下:

import com.huangx.springboot.springboot_autoconfig_demo2.service.OrderService;
import com.huangx.springboot.springboot_autoconfig_demo2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class SpringbootAutoconfigDemo2Application {

    @Autowired
    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(SpringbootAutoconfigDemo2Application.class, args);
    }

    @GetMapping("/")
    public String index() {
        UserService userService = null;
        try {
            userService = applicationContext.getBean(UserService.class);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        OrderService orderService = null;
        try {
            orderService = applicationContext.getBean(OrderService.class);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        return "userService=" + userService + "<br/>" +
                "orderService=" + orderService;
    }

}

运行上面程序后,我们访问 http://localhost:8080 地址,输出如下图:

上图中,我们只获取到了用户服务,没有获取到订单服务对象。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号