@Conditional以及它的家族@ConditionalOnXXX -- 条件选择注入Bean

tech2024-02-19  66

文章目录

前言一、@Conditional的作用1. @Conditional1. Condition接口 二、使用示例1. 编写Condition接口的实现2.添加@Conditional注解3. 编写Test测试类4. 测试结果 三. @ConditionalOnXXX


前言

  有一篇文章:《Spring Boot @Condition 注解,组合条件你知道吗》十分值得阅读


一、@Conditional的作用

1. @Conditional

    @Conditional注解可以作用在创建Bean的类或者方法上,给Spring容器是否加载此bean,添加额外的约束或者判断。

作用在:

类注解(@Component、@Controller、@Service、@Repository)方法注解(@Bean)

的bean都可以用@Conditional做额外约束

    当@Conditional注解的判断条件不满足时,对应的Bean不会生成。

1. Condition接口

    根据上面的介绍,我们知道@Conditional注解是来让Spring容器选择是否加载对应bean的。也就是Spring可以通过**“某种判断”,来确定结果是true或false,进而决定是否加载此bean。     在具体的实现上,“这个判断”**是通过程序员实现Condition接口并重写public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)方法来实现的。

二、使用示例

1. 编写Condition接口的实现

    编写一个我们自己的Condition接口的实现类:ConditionMatchService。这个实现类需要复写返回结果为布尔值的boolean match()方法。在match()方法中,我们编写判断条件:当环境为"condition"字符串时,返回为true;其他为false

/** * @Author: Albert Guo */ public class ConditionMatchService implements Condition { public static final String PROFILE = "condition"; /** * 当此方法返回true时,注解生效 * @param context * @param metadata * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String[] activeProfiles = environment.getActiveProfiles(); if(ArrayUtils.isNotEmpty(activeProfiles)){ for (int i = 0; i < activeProfiles.length; i++) { //当环境为condition时,返回true if (PROFILE.equals(activeProfiles[i])) { return true; } } } return false; } }

2.添加@Conditional注解

  在创建bean的地方添加@Conditional注解条件,并将刚编写的ConditionMatchService类作为判断条件添加

/** * @Author: Albert Guo */ @Configuration public class ConditionConfiguration { /** * 如果ConditionMatchService的match()方法返回true,则ConditionDTO加载,会打印beanName * @return */ @Bean(initMethod="init") @Conditional(ConditionMatchService.class) public ConditionDTO conditionDTO() { return new ConditionDTO(); } }

其中ConditionDTO内容如下,加入了必要的日志:

/** * @Author: Albert Guo */ @Slf4j @Data public class ConditionDTO implements BeanNameAware { //bean的名称 private String beanName; public void init() { log.info("ConditionDTO 开始加载,beanName=={}", beanName); } @Override public void setBeanName(String name) { beanName = name; } }

3. 编写Test测试类

/** * 测试{@code @Conditional}注解 * @Author: Albert Guo */ @Slf4j @ActiveProfiles("dev") @RunWith(SpringRunner.class) @SpringBootTest(classes = {ConditionConfiguration.class}) public class ConditionTest implements ApplicationContextAware { private ApplicationContext applicationContext; @Test public void test() { if(applicationContext.containsBean("conditionDTO")) { log.info("{}的bean存在!", ConditionMatchService.PROFILE); } else { log.warn("{}的bean不存在!", ConditionMatchService.PROFILE); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }

4. 测试结果

当Profile=dev时,可以看到bean并没有加载当Profile=condition时,可以看到bean加载成功

三. @ConditionalOnXXX

  从上面的例子可以看到,@Condition一般要和Condition接口配合使用,还需要重写match()方法,开发起来并不是很精简。有时候编写框架时,设计师更在乎是否存在某个Bean、是否某个包或资源被引入、是否处于某种特殊环境下…对于这种存在与否的判断,可以抽象出某种共性,而不用再底层的每次重写match方法来逐个判断。   因此SpringBoot基于@Condition接口,进一步封装了@ConditionalOnXXX注解。对于这些注解,开发者可以不用再实现Condition接口,而是直接在@ConditionalOnXXX注解的value值中,写入判断的条件(例如class名称、path路径、properties配置等),进一步减少了开发量。 常用到的注解有:

@ConditionalOnBean**(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean) @ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean) @ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean) @ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean) @ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean) @ConditionalOnNotWebApplication(不是web应用)

具体的用法可以参考前言中的文章

最新回复(0)