SpringBoot之@ConditionalOnProperty注解使用方法

 更新时间:2023年05月05日 11:21:11   作者:抢老婆酸奶的小肥仔  
在平时业务中,我们需要在配置文件中配置某个属性来决定是否需要将某些类进行注入,让Spring进行管理,而@ConditionalOnProperty能够实现该功能,文中有详细的代码示例,需要的朋友可以参考下

@ConditionalOnProperty:根据属性值来控制类或某个方法是否需要加载。它既可以放在类上也可以放在方法上。

1、SpringBoot实现

1.1 设置配置属性

在applicatio.properties或application.yml配置isload_bean = true;

#配置是否加载类
is_load_bean: true

1.2 编写加载类

编写加载类,使用@Component进行注解,为了便于区分,我们将@ConditionalOnProperty放在方法上。

/**
 * @author: jiangjs
 * @description: 使用@ConditionalOnProperty
 * @date: 2023/4/24 10:20
 **/
@Component
@Slf4j
public class UseConditionalOnProperty {

    @Value("${is_load_bean}")
    private String isLoadBean;

    @Bean
    @ConditionalOnProperty(value = "is_load_bean",havingValue = "true",matchIfMissing = true)
    public void loadBean(){
        log.info("是否加载当前类");
    }

    @Bean
    public void compareLoadBean(){
        log.info("加载bean属性:" + isLoadBean);
    }
}

启动项目时输出打印的日志。如图:

将配置文件的数据信息改成false,则不会打印出结果。

2、ConditionalOnProperty属性与源码

2.1 属性

查看@ConditionalOnProperty源码可以看到该注解定义了几个属性。

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

	/**
	 * name别名,数组类型,获取对应property名称的值,与name不能同时使用
	 */
	String[] value() default {};

	/**
	 * 属性前缀,未指定时,自动以点"."结束,有效前缀由一个或多个词用点"."连接。
     * 如:spring.datasource
	 */
	String prefix() default "";

	/**
	 * 属性名称,配置属性完整名称或部分名称,可与prefix组合使用,不能与value同时使用
	 */
	String[] name() default {};

	/**
	 * 可与name组合使用,比较获取到的属性值与havingValue的值是否相同,相同才加载配置
	 */
	String havingValue() default "";

	/**
	 * 缺少该配置属性时是否加载,默认为false。如果为true,没有该配置属性时也会正常加载;反之则不会生效
	 */
	boolean matchIfMissing() default false;

}

2.2 源码

查看OnPropertyCondition类中国的getMatchOutcome()方法:

@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//获取所有注解ConditionalOnProperty下的所有属性match,message
        List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
				metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
		List<ConditionMessage> noMatch = new ArrayList<>();
		List<ConditionMessage> match = new ArrayList<>();
        //遍历注解中的属性
		for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
            //创建判定的结果,ConditionOutcome只有两个属性,
			ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
			(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
		}
		if (!noMatch.isEmpty()) {
            //如果有属性没有匹配,则返回
			return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
		}
		return ConditionOutcome.match(ConditionMessage.of(match));
	}

在上述源码中determineOutcome()是关键方法,我们来看看:

private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
	//初始化
    Spec spec = new Spec(annotationAttributes);
    List<String> missingProperties = new ArrayList<>();
    List<String> nonMatchingProperties = new ArrayList<>();
    //收集属性,将结果赋值给missingProperties,nonMatchingProperties
    spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
    if (!missingProperties.isEmpty()) {
        //missingProperties属性不为空,说明设置matchIfMissing的是false,则不加载类
        return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
                .didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
    }
    if (!nonMatchingProperties.isEmpty()) {
        //nonMatchingProperties属性不为空,则设置的属性值与havingValue值不匹配,则不加载类
        return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
                .found("different value in property", "different value in properties")
                .items(Style.QUOTE, nonMatchingProperties));
    }
    return ConditionOutcome
            .match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
}

Spec是OnPropertyCondition的一个静态内部类,初始化@ConditionalOnProperty中的属性。

private static class Spec {

		private final String prefix;

		private final String havingValue;

		private final String[] names;

		private final boolean matchIfMissing;
        //初始化,给各属性赋值
		Spec(AnnotationAttributes annotationAttributes) {
			String prefix = annotationAttributes.getString("prefix").trim();
			if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
				prefix = prefix + ".";
			}
			this.prefix = prefix;
			this.havingValue = annotationAttributes.getString("havingValue");
			this.names = getNames(annotationAttributes);
			this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
		}

        //处理name与value
		private String[] getNames(Map<String, Object> annotationAttributes) {
			String[] value = (String[]) annotationAttributes.get("value");
			String[] name = (String[]) annotationAttributes.get("name");
            //限制了value或name必须指定
			Assert.state(value.length > 0 || name.length > 0,
					"The name or value attribute of @ConditionalOnProperty must be specified");
			//value和name只能有一个存在,不能同时使用
            Assert.state(value.length == 0 || name.length == 0,
					"The name and value attributes of @ConditionalOnProperty are exclusive");
            return (value.length > 0) ? value : name;
		}

		private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
			//遍历names,即value或name的值
            for (String name : this.names) {
                //前缀 + name,获取配置文件中key
				String key = this.prefix + name;
                //验证配置属性中包含key
				if (resolver.containsProperty(key)) {
                    //如包含,则获取key对应的值,与havingValue的值进行匹配
					if (!isMatch(resolver.getProperty(key), this.havingValue)) {
                        //不匹配则添加到nonMatching
						nonMatching.add(name);
					}
				}
				else {
                    //验证配置属性中没有包含key,判断是否配置了matchIfMissing属性
					if (!this.matchIfMissing) {
                        //该属性不为true则添加到missing中
						missing.add(name);
					}
				}
			}
		}

		private boolean isMatch(String value, String requiredValue) {
            //验证requiredValue是否有值
			if (StringUtils.hasLength(requiredValue)) {
                //有值,则进行比较,不区分大小写
				return requiredValue.equalsIgnoreCase(value);
			}
            //没有值,则验证value是否等于false
            //这也是为什么name, value不配置值的情况下, 类依然会被加载的原因
			return !"false".equalsIgnoreCase(value);
		}

		@Override
		public String toString() {
			StringBuilder result = new StringBuilder();
			result.append("(");
			result.append(this.prefix);
			if (this.names.length == 1) {
				result.append(this.names[0]);
			}
			else {
				result.append("[");
				result.append(StringUtils.arrayToCommaDelimitedString(this.names));
				result.append("]");
			}
			if (StringUtils.hasLength(this.havingValue)) {
				result.append("=").append(this.havingValue);
			}
			result.append(")");
			return result.toString();
		}

	}

根据业务需求,我们可以实现配置某些属性动态地去加载某些类或方法。

到此这篇关于SpringBoot之@ConditionalOnProperty注解使用方法的文章就介绍到这了,更多相关SpringBoot @ConditionalOnProperty注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Flutter瀑布流仿写原生的复用机制详解

    Flutter瀑布流仿写原生的复用机制详解

    这篇文章主要给大家介绍了关于Flutter瀑布流仿写原生的复用机制的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用flutter具有一定的参考学习价值,需要的朋友可以参考下
    2021-07-07
  • 线程池FutureTask异步执行多任务实现详解

    线程池FutureTask异步执行多任务实现详解

    这篇文章主要为大家介绍了线程池FutureTask异步执行多任务实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • 通过RedisTemplate连接多个Redis过程解析

    通过RedisTemplate连接多个Redis过程解析

    这篇文章主要介绍了通过RedisTemplate连接多个Redis过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Java中List对象集合按对象中某字段进行排序举例

    Java中List对象集合按对象中某字段进行排序举例

    这篇文章主要给大家介绍了关于Java中List对象集合按对象中某字段进行排序的相关资料,我们在日常开发中也经常会用到排序算法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • 详解eclipse中Maven工程使用Tomcat7以上插件的方法

    详解eclipse中Maven工程使用Tomcat7以上插件的方法

    本篇文章主要介绍了详解eclipse中Maven工程使用Tomcat7以上插件的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • log4j2动态修改日志级别及拓展性使用详解

    log4j2动态修改日志级别及拓展性使用详解

    这篇文章主要介绍了log4j2动态修改日志级别及拓展性使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • 使用IDEA创建maven父子工程项目 (图文)

    使用IDEA创建maven父子工程项目 (图文)

    本文主要介绍了使用IDEA创建maven父子工程项目,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java提取两个字符串中的相同元素方法

    Java提取两个字符串中的相同元素方法

    今天小编就为大家分享一篇Java提取两个字符串中的相同元素方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • Java多线程 Producer and Consumer设计模式

    Java多线程 Producer and Consumer设计模式

    这篇文章主要介绍了Java多线程 Producer and Consumer设计模式,producer是生产者的意思:指生产数据的线程,consumer是消费者的意思,指的是使用数据的线程,下文围绕Producer及Consumer展开话题,需要的朋友可以参考一下
    2021-10-10
  • Java concurrency线程池之线程池原理(二)_动力节点Java学院整理

    Java concurrency线程池之线程池原理(二)_动力节点Java学院整理

    这篇文章主要为大家详细介绍了Java concurrency线程池之线程池原理第二篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06

最新评论