我目前工作的一个弹簧库,允许用户自定义的配置类(有没有什么与@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更改默认配置。在最坏的情况下,用户会尝试自动装配的配置对象,并试图修改它,那是因为其他部件可能已经被用默认值初始化这会导致不确定的行为。
有一个简单的方法,使这种方法更“告诉”比它现在是什么?整个想法是不复制和粘贴多个项目全默认配置+调整部分。
一个方法,使这一切更明确的将需要配置的构造函数的调整,但这种污染inherting类的每一个构造和使用。
在这个有什么想法?
编辑:请注意,这是图书馆的简化版本,我不知道一个私人@PostConstruct等的影响。如果你有实现这一切,而不@PostConstruct请你分享的另一种方式:)
EDIT2:让我再次勾勒出这个库的主要目标:
在两个解决您的问题:
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的唯一的事情就是实现定制,然后把它声明为一个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 ();
}
我觉得这个回答你的问题,但它的丑陋(uncheched警告和一个列表...)不是最好的,因为一切似乎给用户定制即使它不是。
2 - 我建议你暴露接口,可以由用户调整豆,是这样的:
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可以调整(而不是全部)的豆类。