動態修改註解值

背景

平時開發業務時,由於特殊需要,使得一份代碼需要部署到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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章