• 让@EnableConfigurationProperties的值注入到@Value中


    转自:https://boommanpro.blog.csdn.net/article/details/104667530

    需求背景
    定义了一个@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、@Value 、SpringEl 他们之间的关系和区别及我认为的正确使用方式。

    首先@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

  • 相关阅读:
    MJExtension的一些实用技巧
    SDWebImage第三方库使用注意的一些问题
    按钮控件里面的间距问题
    C#实现全窗体范围拖动
    在 C# 中使用文件名启动应用程序
    BOM设计的一些问题及解决方案探讨----合版BOM
    用ASPOSE.Cells将HTML表格存为Excel
    PPT文件太大时可以考虑另存为PPTX格式
    不小心装了个瑞星
    ERP实施顾问--理解客户的解决方案与实际需求
  • 原文地址:https://www.cnblogs.com/hellowzd/p/13891784.html
Copyright © 2020-2023  润新知