【问题标题】:How to create custom annotation similar to @autowire and @value如何创建类似于@autowire 和@value 的自定义注解
【发布时间】:2019-01-25 15:58:54
【问题描述】:

我们在大型 Spring 应用程序中使用来自外部来源的配置值。有没有办法创建自定义注释,以便我们可以“连接”这些配置值提供程序?

我们有一项服务,它根据当前环境 (dev/prod) 和当前渠道 (web/mobile) 等许多变量提供配置值。 目前这使用静态代码并且不使用弹簧。 我搜索了一种使用 spring 注册自定义注释的方法以及该注释的工厂,如下所示:

@MyConfigurationAnnotation(key="my.config.key", fallbackValue= "1")
private MyConfigValueProvider provider;

...
void someMethod(){
    int val = provider.get(currentEnvironment, Integer.class);
}

我正在寻找一种方法将一些“myConfigAnnotationBeanFactory”注册到spring,spring使用注释中的值调用它。然后工厂为这个特定的配置键创建一个供应商 bean。

春天有可能发生这样的事情吗?使用@Autowire 和@Value 已经有两个注释可以做类似的事情,我只想用spring 注册第三种线机制。

【问题讨论】:

    标签: java spring javabeans autowired


    【解决方案1】:

    @ConfigurationProperties 和提供给 @PropertySource 的工厂类的组合是否有助于实现您想要的?

    例如

    @Configuration
    @PropertySource(value="some-value", name="my.config.key", factory=MyConfigFactory.class)
    @ConfigurationProperties(prefix="example")
    public class ExternalConfig {
    
        private String name = "default";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }        
    }
    

    使用工厂类获取您的外部属性

    public class MyConfigFactory implements PropertySourceFactory {
    
        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            // The name will be what you set on the @PropertySource 'my.config.key'
            // The resource is from the value you set on the @PropertySource
            // you could get at that using resource.getResource().getFilename()
            Map<String, Object> properties = new HashMap<>();
            // set whatever properties needed in the map
            properties.put("example.name", name);
            return new MapPropertySource("my-external-properties", properties );
        }
    
    }
    

    这里我刚刚设置了example.name="my.congig.key"。这将替换 ExternalConfig 中 name 字段的初始值“default”。

    【讨论】:

    • 感谢您的回答 :) 这似乎是一个很好的起点。问题是,我不能直接连接原始数据类型,因为我必须在运行时评估环境。如果我让它适用于我的案例,我将对此进行试验并发布
    【解决方案2】:

    我设法找到了一个可行的解决方案: 首先,我为我的配置值创建了一个注释

    @Retention(RUNTIME)
    @Target({ FIELD })
    @Autowired
    public @interface ConfigurationValue {
      String name();
    
      String defaultValue();
    }
    

    然后我为我的配置值添加了一个 BeanPostProcessor/FactoryBean

    public class ConfigValueBeanProcessor implements BeanPostProcessor, FactoryBean<ConfigSupplier> {
    
      @Autowired
      private EnvironmentConfiguration environmentConfiguration;
    
      @Override
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(MyConfigurableMarkerAnnotationOnClassLevel.class)) {
          List<Field> annotatedFields = FieldUtils.getFieldsListWithAnnotation(bean.getClass(), ConfigurationValue.class);
          for (Field field : annotatedFields) {
            try {
              processAnnotatedField(field, bean);
            } catch (IllegalAccessException e) {
            // do stuff
            }
          }
        }
        return bean;
      }
    
      private void processAnnotatedField(Field field, Object bean) throws IllegalAccessException {
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        Object o = field.get(bean);
        if (o instanceof ConfigSupplier) {
          ConfigurationValue annotation = field.getAnnotation(ConfigurationValue.class);
          ConfigSupplier configSupplier = (ConfigSupplier) o;
          ConfigSupplier patchedSupplier = configSupplier.withSettingsKeyAndDefault(
              annotation.name(), annotation.defaultValue());
          field.set(bean, patchedSupplier);
        }
        field.setAccessible(accessible);
      }
    
      @Override
      public ConfigSupplier getObject() throws Exception {
        return new ConfigSupplier(environmentConfiguration);
      }
    
      @Override
      public Class<?> getObjectType() {
        return ConfigSupplier.class;
      }
    }
    

    发生的事情是这样的: Spring 使用所有依赖项自动连接我的 ConfigSupplier。后处理器稍后会在生命周期中使用默认值和正确的配置键修补此供应商。

    感觉有点老套,我仍在寻找更好的替代方案,但它确实有效。最好的方法可能是覆盖 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties 并添加逻辑来创建 bean 并一步添加注释数据,而不是我的两步方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-15
      • 1970-01-01
      相关资源
      最近更新 更多