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

时间:2019-01-25 15:58:54

标签: java spring javabeans autowired

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

我们有一项服务,该服务基于许多变量(例如当前环境(dev / prod)和当前渠道(网络/移动设备))提供配置值。 当前,它使用静态代码,而不使用spring。 我寻找了一种在spring中注册自定义注释的方法,以及为该注释注册工厂的方法,如下所示:

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

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

我正在寻找一种注册一些“ myConfigAnnotationBeanFactory”的方法,该方法会使用注释中的值进行弹簧调用。然后,工厂为此特定的配置密钥创建一个供应商bean。

春季有可能发生这种情况吗?有了@Autowire和@Value,已经有两个注释可以做类似的事情,我只想在spring中注册第三种电线机制。

2 个答案:

答案 0 :(得分: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”。

答案 1 :(得分:0)

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

@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并一步一步添加注释数据,而不是我的两步方法。