栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Boot 2.x系列【14】功能篇之@Conditional注解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Boot 2.x系列【14】功能篇之@Conditional注解

有道无术,术尚可求,有术无道,止于术。

本系列Spring Boot版本2.7.0

文章目录
    • 前言
    • @Conditional
    • @ConditionalOnBean
    • @ConditionalOnMissingBean
    • @ConditionalOnClass
    • @ConditionalOnMissingClass
    • @ConditionalOnWebApplication
    • @ConditionalOnProperty
    • @Profile
    • 其他注解

前言

在上篇文档中,我们回顾了Spring 中创建Bean 的各种方式,如果我们需要根据其他条件或者配置项来控制是否注册一个Bean,应该怎么办呢?

如果是通过@Import导入一个选择器,获取条件再判断是否注册,倒是可以实现,但是十分麻烦。

Spring Boot已经为我们提供了@Conditional及一系列的派生注解,实现条件控制。

@Conditional

conditional翻译过来是【有条件的; 条件的; 附带条件的; 依…而定的】意思,就和if else条件判断语句一样,条件成立时,才进行Bean 的注册。

@Conditional是Spring 原生框架提供的注解,需要配置一个Condition接口的实现类:

public @interface Conditional {
    Class[] value();
}

在Condition接口中,声明了一个匹配方法,返回真假,为真时进行注册,反之不注册:

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

案例演示:需要根据一个自定义的配置项来决定是否注册一个Bean 。

首先实现Condition接口,在匹配方法中,查询当前环境中是否配置了某个配置项:

public class MyCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取属性 condition
        String condition = context.getEnvironment().getProperty("condition");
        return StringUtils.hasText(condition);
    }
}

在需要配置该条件类上,添加@Conditional注解:

@Component
@Conditional(MyCondition.class)
public class TestCondition {
}

可以发现,只有在YML 中配置了condition时,才会注册该Bean。

condition: hahaha

也可以自定义一个派生条件注解,使用更加的方便:

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

除了写在类上,还可以直接写上方法上:

    @Bean
    @ConditionalOnMyProperty
    TestCondition testCondition(){
        return new TestCondition();
    }

Spring Boot 已经为我们提供了直接使用的条件注解,并在自动配置源码中大量运用:

接下来挑一些常用的注解进行说明和演示。

@ConditionalOnBean

在上下文中存在某个Bean 对象时,才会注入当前Bean 。

源码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {
	// 条件Bean Class 数组
    Class[] value() default {};
	// 条件Bean 的类名,Class.getName()
    String[] type() default {};
	// 使用某个注解的条件Bean 
    Class[] annotation() default {};
	// 条件Bean 的Bean 名称
    String[] name() default {};
	// 条件Bean 容器层级
	// ALL:所有容器
    // ANCESTORS:所有祖先容器,但不包含当前容器
    // CURRENT:仅当前容器
    SearchStrategy search() default SearchStrategy.ALL;
	// 条件Bean 的类型为指定类型,并且泛型为当前需要注入的Bean 
    Class[] parameterizedContainer() default {};
}
@ConditionalOnMissingBean

在上下文中不存在某个Bean 对象时,才会注入当前Bean ,是@ConditionalOnBean的反例。

配置属性同@ConditionalOnBean:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnMissingBean {
    Class[] value() default {};

    String[] type() default {};

    Class[] ignored() default {};

    String[] ignoredType() default {};

    Class[] annotation() default {};

    String[] name() default {};

    SearchStrategy search() default SearchStrategy.ALL;

    Class[] parameterizedContainer() default {};
}
@ConditionalOnClass

某个class存在于类路径上,才会注入当前Bean 。

源码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
	// 指定条件类的Class 类型
    Class[] value() default {};
	// 指定条件类的类名
    String[] name() default {};
}

在Spring Boot 自动配置类中,大量使用了该注解,比如引入了MongoDB 客户端包,才会触发MongoDB 的自动配置。

@ConditionalOnMissingClass

某个class不存在于类路径上,才会注入当前Bean ,是@ConditionalOnClass的反例。

只能配置一个类名属性:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnMissingClass {
    String[] value() default {};
}
@ConditionalOnWebApplication

当Web 应用程序和指定的类型一致时,才会注入当前Bean ,如果某些Bean 在指定的Web 程序类型时才能使用,这很有效。

比如当前为SERVLET类型的Web 容器,才会注册TestCondition 。

@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class TestCondition {
}

@ConditionalOnNotWebApplication则相反。

@ConditionalOnProperty

存在某个配置属性值时,才会注入当前Bean,可以在配置文件中添加一个开关属性,实现是否加载当前Bean 的功能。

源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
	// 对应属性值为真或假,可配置多个,一个为假,都不注册
	// 和 name() default {}; 不可同时存在
    String[] value() default {};
	// 属性名前缀
    String prefix() default "";
	// 属性完整名称或部分名称,可与prefix组合使用,组成完整的配置属性名称
    String[] name() default {};
	// 是否有属性值
    String havingValue() default "";
	// 未配置havingValue的值时,是否加载当前Bean 
    boolean matchIfMissing() default false;
}

比如我们在配置文件中添加一个配置,用于判定是否开启某个功能:

spring:
  h2:
    console:
      enabled: true

可以使用以下两种方式配置:

@ConditionalOnProperty(value = {"spring.h2.console.enabled"})

@ConditionalOnProperty(
    prefix = "spring.h2.console",
    name = {"enabled"},
    havingValue = "true",
    matchIfMissing = false
)
@Profile

@Profile可以根据当前激活的环境决定是否注册某个Bean。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
    String[] value();
}

这是非常常用的一注解,比如写了一个打印开发日志的切面,切换到测试或者生产环境时,需要关闭,这个时候就可以在切面上添加@Profile,添加一个dev属性,表示只有当spring.profiles.active配置为开发环境时,这个切面才有效。

@Aspect
@Profile({"dev"})
public class RequestLogAspect {
// 切面
}
其他注解
  • @ConditionalOnExpression:当SpEL 表达式为true 。

  • @ConditionalOnJava:当JVM版本为指定的版本范围时。

  • @ConditionalOnResource:当类路径下有指定的资源时。

  • @ConditionalOnJndi:在JNDI存在时。

  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1025099.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号