如何让@EnableConfigurationProperties的值注入到@Value中

 更新时间:2025年06月07日 10:15:28   作者:Boom_Man  
这篇文章主要介绍了如何让@EnableConfigurationProperties的值注入到@Value中的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

需求背景

定义了一个@ConfigurationProperties的配置类,然后在其中定义了一些定时任务的配置,如cron表达式,因为项目会有默认配置,遂配置中有默认值

大体如下:

@Data
@Validated
@ConfigurationProperties(value = "task")
public class TaskConfigProperties {
    /**
     * 任务A在每天的0点5分0秒进行执行
     */
     @NotBlank
    private String taskA = "0 5 0 * * ? ";

}

定时任务配置:

    @Scheduled(cron = "${task.task-a}")
    public void finalCaseReportGenerate(){
        log.info("taskA定时任务开始执行");
        //具体的任务
        log.info("taskA定时任务完成执行");
    }

但是如上直接使用是有问题的${task.taskA}是没有值的,必须要在外部化配置中再写一遍,这样我们相当于默认值就没有用了,这怎么行呢,我们来搞定他。

探究其原理

@ConfigurationProperties@ValueSpringEl 他们之间的关系和区别及我认为的正确使用方式。

首先@ConfigurationProperties 是Spring Boot引入的,遂查询官方文档的讲解

Spring Boot -> Externalized Configuration

我们发现外部化配置中没有值的话,报错是在org.springframework.util.PropertyPlaceholderHelper#parseStringValue

其中org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver是解析的关键

我们只要把默认值装载到系统中,让org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver#resolvePlaceholder可以解析到就可以了

遂我们可以把值装载到Environment中

/**
 * @author wangqimeng
 * @date 2020/3/4 0:04
 */
@Data
@Slf4j
@Validated
@ConfigurationProperties(prefix = "task")
public class TaskConfigProperties implements InitializingBean , EnvironmentPostProcessor {

    /**
     * 任务A在每天的0点5分0秒进行执行
     */
    @NotBlank
    private String taskA = "0 5 0 * * ? ";

    @Value("${task.task-a}")
    public String taskAValue;

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("taskAValue:{}",taskAValue);
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        log.info("TaskConfigProperties-> postProcessEnvironment 开始执行");
        //取到当前配置类上的信息
        MutablePropertySources propertySources = environment.getPropertySources();
        Properties properties = new Properties();
        if (taskA != null) {
            properties.put("task.task-a", this.taskA);
        }
        PropertySource propertySource = new PropertiesPropertySource("task", properties);
        //即优先级低
        propertySources.addLast(propertySource);
    }
}

需要在META-INF -> spring.factories中配置

org.springframework.boot.env.EnvironmentPostProcessor=\
cn.boommanpro.config.TaskConfigProperties

所以addLast是优先级最低的,让我们新加入的配置优先级最低。

以上就简单的完成了我们的需求。

最终实现

配置类中的有默认值的不需要在External Configuration中再度配置

通过一个注解@EnableBindEnvironmentProperties,绑定含有@ConfigurationPropertiesClass的默认值到Environment

  • @EnableBindEnvironmentProperties
/**
 * @author wangqimeng
 * @date 2020/3/4 1:21
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableBindEnvironmentProperties {


    Class<?>[] value() default {};
}
  • @EnableBindEnvironmentPropertiesRegister
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;

/**
 * @author wangqimeng
 * @date 2020/3/4 15:11
 */
@Slf4j
public class EnableBindEnvironmentPropertiesRegister implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        EnableBindEnvironmentProperties annotation = application.getMainApplicationClass().getAnnotation(EnableBindEnvironmentProperties.class);
        Arrays.stream(annotation.value())
                .forEach(aClass -> registerToEnvironment(propertySources, aClass));
    }

    public void registerToEnvironment(MutablePropertySources propertySources, Class<?> clazz) {
        ConfigurationProperties annotation = clazz.getAnnotation(ConfigurationProperties.class);
        if (annotation == null) {
            return;
        }
        String prefix = annotation.prefix();
        String name = String.format("%s-%s", prefix, clazz.getName());
        try {
            Properties properties = toProperties(prefix, clazz.newInstance());
            PropertySource propertySource = new PropertiesPropertySource(name, properties);
            propertySources.addLast(propertySource);
        } catch (Exception e) {
            log.error("Exception:", e);
            throw new RuntimeException();
        }

    }

    public Properties toProperties(String prefix, Object o) throws Exception {
        Properties properties = new Properties();
        Map<String, Object> map = objectToMap(o);
        map.forEach((s, o1) -> {
            properties.put(String.format("%s.%s", prefix, camelToUnderline(s)), o1);
        });

        return properties;
    }

    public static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append("-");
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public static Map<String, Object> objectToMap(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>(10);
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            if (value == null) {
                continue;
            }
            map.put(key, value);
        }

        return map;
    }
}

配置到META-INF/spring.factories

# Application Listeners
org.springframework.boot.env.EnvironmentPostProcessor=\
cn.boommanpro.annotation.EnableBindEnvironmentPropertiesRegister

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java 线程池预热(Warm-up)实战

    Java 线程池预热(Warm-up)实战

    本文主要介绍了Java 线程池预热(Warm-up)实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-02-02
  • Java实现使用Websocket发送消息详细代码举例

    Java实现使用Websocket发送消息详细代码举例

    这篇文章主要给大家介绍了关于Java实现使用Websocket发送消息的相关资料,WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05
  • SpringBoot下实现session保持方式

    SpringBoot下实现session保持方式

    这篇文章主要介绍了SpringBoot下实现session保持方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java自带的MessageDigest实现文本的md5加密算法

    java自带的MessageDigest实现文本的md5加密算法

    这篇文章主要介绍了java自带的MessageDigest实现文本的md5加密算法,需要的朋友可以参考下
    2015-12-12
  • SpringBoot @FunctionalInterface注解的项目实战

    SpringBoot @FunctionalInterface注解的项目实战

    本文主要介绍了Java中的@FunctionalInterface注解及其在SpringBoot项目中的应用,包括如何使用函数式接口和Lambda表达式简化代码,感兴趣的可以了解一下
    2025-11-11
  • drools中使用function的方法小结

    drools中使用function的方法小结

    当我们在drools中编写规则时,有些时候存在重复的代码,那么我们是否可以将这些重复代码抽取出来,封装成一个function来调用呢?那么在drools中如何自定义function?下面小编给大家介绍下drools中使用function的方法,需要的朋友可以参考下
    2022-05-05
  • SpringBoot集成Open WebUI实现AI流式对话

    SpringBoot集成Open WebUI实现AI流式对话

    本文介绍如何在 Spring Boot 项目中集成 Open WebUI,通过自动管理用户 Token、调用 OpenAI Java SDK,实现 SSE 流式输出,并与前端文本输入框无缝对接,为业务系统注入 AI 能力,需要的朋友可以参考下
    2026-05-05
  • SpringMvc(Interceptor,Filter)实现方案

    SpringMvc(Interceptor,Filter)实现方案

    在JavaWeb开发中,过滤器和拦截器都是用于实现AOP的工具,但它们在框架层级和执行时机上有所不同,过滤器属于Servlet规范,位于所有Servlet之前,可以对所有请求进行预处理,本文给大家介绍SpringMvc(Interceptor,Filter)实现方案,感兴趣的朋友一起看看吧
    2026-01-01
  • java string的一些细节剖析

    java string的一些细节剖析

    首先说明这里指的是Java中String的一些细节部分,需要的朋友可以参考
    2012-11-11
  • Java Predicate接口源码使用示例

    Java Predicate接口源码使用示例

    Java8引入了许多函数式接口(Functional Interface),Predicate(断言)就是其中一个,它的主要作用可以简单描述为:向其传入一个对象(可以理解为参数),将得到一个布尔值作为输出,这篇文章主要介绍了Java Predicate接口源码使用示例,需要的朋友可以参考下
    2025-04-04

最新评论