为可扩展的配置对象提高spring库的显式性

时间:2019-02-01 11:05:45

标签: java spring design-patterns configuration

我目前正在开发一个弹簧库,该库允许在使用它们之前从应用程序的另一部分调整用户定义的配置类(与@Configuration无关):

interface ConfigAdjuster<T extends Config<T>> {
    void adjust(T t);
}

abstract class Config<T extends Config<T>> {
     @Autowired
     Optional<ConfigAdjuster<T>> adjuster;

     @PostConstruct
     private void init() {
         //i know this cast is somewhat unsafe, just ignore it for this question
         adjuster.ifPresent(a -> a.adjust((T)this));
     }
}

可以如下使用:

class MyConfig extends Config<MyConfig> {
    //imagine many fields of more complex types
    public String myData;
}

@Configuration
class MyConfigDefaults {
    @Profile("dev")
    @Bean 
    public MyConfig devDefaults() {
        //imagine setting defaults values here
        return new MyConfig();
    }
}

现在,使用MyConfig的库的使用者可以在其应用程序中的某处执行以下操作:

@Bean
public ConfigAdjuster<MyConfig> adjustDefaults() {
    return cfg -> {
        cfg.myData = "something_other_than_default";
    }
}

我用这种方法看到的最大问题是,整个“调整配置”部分对于用户来说都是隐藏的。您不能轻易地说出您可以使用ConfigAdjuster更改默认配置。在最坏的情况下,用户尝试自动连接配置对象并尝试以这种方式进行修改,这会导致行为未定义,因为其他组件可能已经使用默认值进行了初始化。

是否有一种简单的方法可以使这种方法比现在更“讲”?整个想法是不要在多个项目中复制并粘贴整个default-config +调整部分。

使所有这些内容更加明确的一种方法是在Config的构造函数中需要调节器,但这会污染每个构造函数和继承类的使用。

对此有何想法?

编辑:请注意,这是该库的简化版本,我确实知道私有@PostConstruct等的含义。如果您有另一种无需@PostConstruct即可实现所有这些功能的方法,请共享:)< / p>

Edit2: 让我再次概述该库的主要目标:

  1. 允许为库用户定义默认配置对象
  2. 允许最终用户(使用此库消耗大量资源)在使用默认配置之前覆盖其一部分
  3. 从样板中保存库用户(例如,单独定义2。)

1 个答案:

答案 0 :(得分:2)

您的问题有两种解决方案:

1-定义通用自定义程序,例如:

public interface Customizer<T> {

    T customize(T t);

    boolean supports(Class target);
}

在您的lib中,您有一个配置:

public class MyConfig {

    private String property;

    public MyConfig() {
    }

    public void setProperty(String property) {
        this.property = property;
    }
}

因此您的默认配置应如下所示:

@Configuration
public class DefaultConfiguration {


    @Autowired(required = false)
    private List<Customizer> customizers;

    @Bean
    public MyConfig myConfig() {
        MyConfig myConfig = new MyConfig();
        myConfig.setProperty("default value");
        if (customizers != null) {     
          for (Customizer c : customizers) {
            if (c.supports(MyConfig.class)) {
                return (MyConfig) c.customize(myConfig);
            }
          }
        }
        return myConfig;
    }
}

这样,用户想要自定义bean时唯一要做的就是实现Customizer,然后将其声明为bean。

public class MyConfigCustomizer implements Customizer<MyConfig> {

    @Override
    public MyConfig customize(MyConfig myConfig) {
        //customization here
        return myConfig;
    }

    @Override
    public boolean supports(Class<?> target) {
        return MyConfig.class.isAssignableFrom(target);
    }
}

他应该声明:

@Bean 
public Customizer<MyConfig> customizer(){
     return new MyConfigCustomizer ();
 }

我认为这可以回答您的问题,但这不是很丑陋的(未发出警告和列表...),因为用户似乎可以自定义所有内容

2-我建议您公开可由用户调整的Bean接口,例如:

public interface MyConfigCustomizer{

MyConfig customize(MyConfig config);

}

您的默认配置:

@Configuration
public class DefaultConfiguration {


    @Autowired(required = false)
    private MyConfigCustomizer customizer;

    @Bean
    public MyConfig myConfig() {
        MyConfig myConfig = new MyConfig();
        myConfig.setProperty("default value");
        if (customizer != null) {
            return customizer.customize(myconfig);
        }
        return myConfig;
    }

}

通过这种方式,用户知道可以调整MyConfig(而不是所有bean)。