背景
平時開發業務時,由於特殊需要,使得一份代碼需要部署到4個應用服務中爲不同對象進行服務。公司使用的配置中心是自研發的,暫不支持配置中心註解(如:@DynamicPropertyInject(name = "${application.effective.exchanges}")
)的值採用properties
文件來動態配置。這使得在同套代碼的不同應用場景下,表明同個含義的注入就需要聲明多次,且有且僅只有一個聲明對於當前應用是有效的。從簡潔的角度來說不適合。
示例註解:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD}
public @interface DynamicPropertyInject {
String name() default '';
}
不能依據properties
注入的代碼情形:
public class DynamicConfiguration {
@DynamicPropertyInject(name = "application1.effective.name")
private String application1ExchangeName;
@DynamicPropertyInject(name = "application2.effective.name")
private String application2ExchageName;
@DynamicPropertyInject(name = "application3.effective.name")
private String application3ExchangeName;
}
其實在每個應用中,僅只有application1ExchangeName
或application2ExchangeName
或application3ExchangeName
其中之一有效。
能依據properties
注入的代碼情形:
public class DynamicConfiguration {
@DynamicPropertyInject(name = "${application.effective.name}")
private String applicationExchangeName;
}
解決:
針對如上所述的場景,其實要解決的是來獲取applicationExchangeName
的時候,需要根據當前的應用名稱來取其對應的值。很自然的,第一反應是通過反射來獲取註解屬性的值並修改之。
在這裏提供下思路:
- 爲
DynamicConfiguration
的類定義切面; - 程序執行到註解
DynamicPropertyInject
時,判斷是否需要去修改值。是則修改,否則跳過。
public class DynamicConfigValueAspect {
public static final String CHARACTER_DOLLAR = "$";
public static final String CHARACTER_NUMBERSIGN = "#";
@Autowired
private ApplicationProperties applicationProperties;
@Around("this(package of DynamicConfiguration)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
if (methodName.startsWith("get") && methodName.length() >= 4) {
//提取屬性名
String fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
Field field = pjp.getTarget().getClass().getDeclaredField(fieldName);
DynamicPropertyInject configuration = field.getAnnotation(DynamicPropertyInject.class);
String configName = configuration.name();
//屬性的DynamicPropertyInject註解值是否需要修改
if (StringUtils.isNotBlank(configName) && StringUtils.startsWith(configName, CHARACTER_DOLLAR) || StringUtils.startsWith(configName, CHARACTER_NUMBERSIGN)) {
InvocationHandler invocationHandler = Proxy.getInvocationHandler(configuration);
Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues");
declaredField.setAccessible(true);
Map memberValues = (Map) declaredField.get(invocationHandler);
//獲取修改的值並修改
memberValues.put("name", getApplicationPropertiesByConfigElement(configName));
}
}
return pjp.proceed();
}
//獲取修改的值
private String getApplicationPropertiesByConfigElement(String configName) throws IllegalAccessException {
Field[] fields = ApplicationProperties.class.getDeclaredFields();
if (ArrayUtils.isEmpty(fields)) {
return null;
}
for (Field field : fields) {
Value annotation = field.getAnnotation(Value.class);
if (Objects.nonNull(annotation) && StringUtils.equals(configName, annotation.value())) {
field.setAccessible(true);
return String.valueOf(field.get(applicationProperties));
}
}
return null;
}
}
//application property
@Configuration
public class ApplicationProperties {
@Value("${application.effective.name}")
private String applicationEffectiveName;
}