繁体   English   中英

如何创建类似于@autowire 和@value 的自定义注解

[英]How to create custom annotation similar to @autowire and @value

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

我们有一个服务,它根据当前环境(开发/生产)和当前频道(网络/移动)等许多变量提供配置值。 目前这使用静态代码并且不使用弹簧。 我搜索了一种使用 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 注册第三种线机制。

@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”初始值。

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

@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 和所有依赖项。 后处理器稍后在生命周期中使用默认值和正确的配置密钥修补此供应商。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM