先來看看通常情況下,我們通過反射擷取註解的值的情境:
那麼現在我們定義一個 @Foo 註解,它有一個類型為 String 的 value 屬性,該註解應用再Field上:
/** * @Author 落葉飛翔的蝸牛 * @Date 2018/3/11 * @Description */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Foo { String value();}
再定義一個普通的Java對象 Bar,它有一個私人的String屬性 value,並為它設定屬性值為"test.annotation.value" 的 @Foo 註解
/** * @Author 落葉飛翔我蝸牛 * @Date 2018/3/11 * @Description */public class Bar { @Foo("test.annotation.value") private String value;}
正常的擷取註解屬性值的情境:
/** * @Author 落葉飛翔的蝸牛 * @Date 2018/3/10 * @Description */@RunWith(SpringRunner.class)public class ReflectionAnnotationTest { @Test public void test() throws NoSuchFieldException, IllegalAccessException { //擷取Bar執行個體 Bar bar = new Bar(); //擷取Bar的val欄位 Field field = bar.getClass().getDeclaredField("value"); //擷取val欄位上的Foo註解執行個體 Foo foo = field.getAnnotation(Foo.class); //擷取Foo註解執行個體的 value 屬性值 String value = foo.value(); //列印該值 System.out.println("修改之前的註解值:" + value); }}
我們在上面的String value = foo.value(); 處下斷點,我們跑一下可以發現當前棧中有這麼幾個變數,不過其中有一點很特別:foo,其實是個Proxy執行個體。
看到foo棧中的屬性h是一個AnnotationInvocationHandler類型的對象。h對象中包含一個memberValues對象,裡面裝著key就是我們自訂註解的屬性,value就是我賦的值。我們看一下AnnotationInvocationHandler類的源碼:
class AnnotationInvocationHandler implements InvocationHandler, Serializable { private static final long serialVersionUID = 6182022883658399397L; private final Class<? extends Annotation> type; private final Map<String, Object> memberValues;
可以發現memberValues對象是private final修飾的。所以我們可以通過反射修改memberValues的存取權限,來打到修改memberValues值的目的。
所以動態修改註解的值的方法為:通過反射得到foo的代理對象,然後得到代理對象的memberValues屬性,修改存取權限,更新註解的value屬性值。修改後的代碼如下:
/** * @Author 落葉飛翔的蝸牛 * @Date 2018/3/10 * @Description */@RunWith(SpringRunner.class)public class ReflectionAnnotationTest { @Test public void test() throws NoSuchFieldException, IllegalAccessException { //擷取Bar執行個體 Bar bar = new Bar(); //擷取Bar的val欄位 Field field = bar.getClass().getDeclaredField("value"); //擷取val欄位上的Foo註解執行個體 Foo foo = field.getAnnotation(Foo.class); //擷取Foo註解執行個體的 value 屬性值 String value = foo.value(); //列印該值 System.out.println("修改之前的註解值:" + value); System.out.println("------------以下是修改註解的值------------"); //擷取 foo 這個代理執行個體所持有的 InvocationHandler InvocationHandler invocationHandler = Proxy.getInvocationHandler(foo); // 擷取 AnnotationInvocationHandler 的 memberValues 欄位 Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues"); // 因為這個欄位事 private final 修飾,所以要開啟許可權 declaredField.setAccessible(true); // 擷取 memberValues Map memberValues = (Map) declaredField.get(invocationHandler); // 修改 value 屬性值 memberValues.put("value", "test.annotation.new.value"); // 擷取 foo 的 value 屬性值 String newValue = foo.value(); System.out.println("修改之後的註解值:" + newValue); }}