Java反射動態修改註解的值
阿新 • • 發佈:2019-02-08
先來看看通常情況下,我們通過反射獲取註解的值的場景:
那麼現在我們定義一個 @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);
}
}