我有以下弹簧属性
myservice:
domains:
domain1:
supplier:
baseUrl:
getEndpoint:
customer:
postEndpoint:
domain2:
supplier:
baseUrl:
getEndpoint:
retryCount:
customer:
baseUrl:
postEndpoint:
fallbackDomain:
supplier:
baseUrl:
getEndpoint:
retryCount:
customer:
baseUrl:
postEndpoint:
这个想法是多个域有恒定的配置,可以是“domain1”,“domain2”,“domain3”等。此外还有后备域。 如果在特定域上找不到属性,则使用后备值。 示例:
我正在使用 spring boot 2.3.3 和 spring 5.2.8,但如果需要的话考虑升级。
选项之一是从 org.springframework.core.env.Environment 获取属性,例如
public <T> T getPropertyWithFallback(String nameTemplate, String domain, Class<T> clazz){
return Optional.ofNullable(environment.getProperty(nameTemaplte.format(domain), clazz))
.orElse(environment.getProperty(nameTemplate.format("fallbackDomain"), clazz));
}
int count = getPropertyValueWithFallback("myservice.domains.%s.supplier.retryCount", "domain1", Integer.class);
但是然后:
我可以使用 spring 配置属性功能,例如:
@Component
@ConfigurationProperties(prefix = "myservice")
public class MyServiceConfigurationProperties {
// skipping gettters and setters for readability
private Map<String, Domain> domains;
public static class Domain {
private Supplier supplier;
private Customer customer;
}
public static class Supplier {
private String baseUrl;
private String getEndpoint;
private Integer retryCount;
}
public static class Customer {
private String baseUrl;
private String postEndpoint;
}
}
这工作正常,配置后我不需要记住属性名称,类型已经分配。浏览/列出域的所有属性很容易。
我尝试使用代理来实现它
private static class FallbackInvocationHandler implements InvocationHandler {
private final Object primary;
private final Object fallback;
public FallbackInvocationHandler(Object primary, Object fallback) {
this.primary = primary;
this.fallback = fallback;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(primary, args);
if (result == null) {
result = method.invoke(fallback, args);
}
if (result != null && result.getClass().getPackage().getName().startsWith("no.eg.apps.pda6k.server.config")) {
return Proxy.newProxyInstance(
result.getClass().getClassLoader(),
new Class[]{result.getClass()},
new FallbackInvocationHandler(result, method.invoke(fallback, args))
);
}
return result;
}
}
private <T> T getWithFallback(T value, T fallbackValue){
if (value==null){
return fallbackValue;
}
return (T) Proxy.newProxyInstance(
value.getClass().getClassLoader(),
new Class[]{value.getClass()},
new FallbackInvocationHandler(value, fallbackValue)
}
// and then each getter implement in such way
public Domain getDomainWithFallback(String domain){
Domain value = domains.get(domain);
Domain fallbackValue = domains.get("fallbackDomain");
return getWithFallback(value, fallbackValue);
}
嵌套属性变得更加复杂/容易出错。 “乐观”的实施可能看起来像:
// witin Supplier class
public Integer getRetryCountWithFallback(){
return getWithFallback(this.retryCount, domains.get("fallbackValue").getSupplier().getRetryCount());
}
但是如果 fallbackDomain 没有/空 supplier 属性怎么办 - 需要一些额外的空检查,这会使所有简单事情的代码变得复杂。 添加新属性也变得更加复杂。 如果存在重复属性,例如是否可以重用配置结构?客户和供应商具有相同的结构,我想为他们使用相同的 java 对象?
您是否看到比上面提供的更好的解决方案可以让我解决问题?我不想改变属性的结构,但如果需要的话,这是一个选项。
解决方案2现在已经很好了。但让我们跳出框框思考一下。您的域名到底是什么?这是一个服务、一个获取数据的地方、一个代码函数吗?其中每种不同的类型您可以有不同的解决方案。例如,在某些情况下,我可以应用简单的套接字线程来检查某些服务的运行状态。这种方法可以稍微修改一下并适用于很多问题。