背景
平时开发业务时,由于特殊需要,使得一份代码需要部署到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;
}