动态修改注解值

背景

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

其实在每个应用中,仅只有application1ExchangeNameapplication2ExchangeNameapplication3ExchangeName其中之一有效。

能依据properties注入的代码情形:

public class DynamicConfiguration {
    @DynamicPropertyInject(name = "${application.effective.name}")
    private String applicationExchangeName;
}

解决:

针对如上所述的场景,其实要解决的是来获取applicationExchangeName的时候,需要根据当前的应用名称来取其对应的值。很自然的,第一反应是通过反射来获取注解属性的值并修改之。

在这里提供下思路:

  1. DynamicConfiguration的类定义切面;
  2. 程序执行到注解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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章