java - 提高可扩展配置对象的 spring 库的显式性
问题描述
我目前正在开发一个spring-library,它允许在使用之前从应用程序的另一部分调整用户定义的配置类(与@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 更改默认配置。在最坏的情况下,用户尝试自动装配配置对象并尝试以这种方式修改它,这会导致未定义的行为,因为其他组件可能已经使用默认值进行了初始化。
有没有一种简单的方法可以使这种方法比现在更“有说服力”?整个想法是不要在多个项目中复制和粘贴整个默认配置 + 调整部分。
使所有这些更明确的一种方法是在 Config 的构造函数中要求调整器,但这会污染每个构造函数和继承类的使用。
对此有什么想法吗?
编辑:请注意,这是该库的简化版本,我确实知道私有 @PostConstruct 等的含义。如果您有其他方法可以在没有 @PostConstruct 的情况下实现所有这些,请分享:)
Edit2:让我再次概述这个库的主要目标:
- 允许为库用户定义默认配置对象
- 允许最终用户(使用此库使用依赖项)在使用默认配置之前覆盖默认配置的某些部分
- 从样板文件中保存库用户(例如,自行定义 2.)
解决方案
There is two solution for your problem:
1- define a generic Customizer something like:
public interface Customizer<T> {
T customize(T t);
boolean supports(Class target);
}
in your lib you have a config:
public class MyConfig {
private String property;
public MyConfig() {
}
public void setProperty(String property) {
this.property = property;
}
}
so your Default configuration should look something like this:
@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;
}
}
this way, the only thing the user should do whenever he wants to customize you bean is to implement Customizer, and then declare it as a 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);
}
}
and he should declare it:
@Bean
public Customizer<MyConfig> customizer(){
return new MyConfigCustomizer ();
}
I think this answers your question, but it's ugly (uncheched warnings and a List ...) not the best, as everything seems to the user customizable even it's not.
2- I suggest you expose interfaces for Beans that can be adjusted by the user, something like:
public interface MyConfigCustomizer{
MyConfig customize(MyConfig config);
}
your Default Configuration:
@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;
}
}
this way the user knows that MyConfig can be adjusted (and not all the beans).
推荐阅读
- javascript - 无法在 VS 中的 C# ASP.NET MVC 应用程序中加载 Content & Script 文件夹中的任何文件
- mysql - brew启动旧版本的mysql服务,但是如何连接呢?
- matlab - MATLAB App Designer UIAxes Plot 完全空白
- compilation - NoClassDefFoundError:生成apk时出错
- ruby - 何时将 self 用于 Ruby 类
- algorithm - 如何平衡多个消息队列
- r - 我想用标记的椭圆和组的部分向量绘制 FactoMineR MFA
- multithreading - Python线程一次不运行2个进程
- react-native - 如何在 react-native-signature-capture 中获取保存的签名图像
- shell - Shell:如果..elif 条件无法按要求创建文件