其實早先是查到動態修改註解值的方法,然後才慢慢去了解動態代理的,但既然寫文章做總結,最好是從原理開始。
一個簡單例子
package main.java.proxy;
/**
* 隨便弄個接口
*
*/
public interface Returnable {
public void fun();
}
package main.java.proxy;
/**
* 實現一下
*
*/
public class ReturnableBean implements Returnable {
public void fun() {
System.out.println("fun");
}
public ReturnableBean() {
}
}
package main.java.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
/**
* {@code proxy}是一個內部類對象,這個內部類是動態生成的,例如{@code com.sun.proxy.$Proxy0},該案例裏面這個內部類是實現接口{@code main.java.proxy.Returnable},有成員變量{@code InvocationHandler},
* 所有的接口方法內部,包括{@code fun},均調用成員變量{@code InvocationHandler}的{@code invoke}方法。
*
*/
public static void main(String[] args) throws NoSuchMethodException,
SecurityException {
ReturnableBean reBean = new ReturnableBean();
Returnable proxy = (Returnable) Proxy.newProxyInstance(
ReturnableBean.class.getClassLoader(),
new Class[] { Returnable.class }, new MyInvocationHandler(
reBean));
proxy.fun();
}
/**
* 內部類實現的接口方法裏面,都是調用成員變量{@code InvocationHandler}的{@code invoke}方法,可以在{@code invoke}加需要額外執行的邏輯,然後再調用被代理類{@code ReturnableBean}的對象方法。
*
*/
public static class MyInvocationHandler implements InvocationHandler {
private ReturnableBean reBean;
public MyInvocationHandler(ReturnableBean reBean) {
this.reBean = reBean;
}
public Object invoke(Object paramObject, Method paramMethod,
Object[] paramArrayOfObject) throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
System.out.println("invoke");
return paramMethod.invoke(reBean, paramArrayOfObject);
}
}
}
執行結果:
invoke
fun
這裏,動態的作用是發生於接口類Returnable
換了一個,當然了,對應的接口方法也變了,那麼就無需像普通代理一樣,再編寫一個代理類,代理類由JDK動態生成,同時保留並複用了代理功能System.out.println("invoke");
,這裏只要對上面的例子稍作修改。
public static class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
public Object invoke(Object paramObject, Method paramMethod,
Object[] paramArrayOfObject) throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
System.out.println("invoke");
return paramMethod.invoke(object, paramArrayOfObject);
}
}
這樣就可以代理任意接口類子類的對象object
。
其實中文描述爲動態代理不是很方便理解,動態只是實現手段,真正目的是爲了代理內容的複用性。
對於註解
public static Annotation annotationForMap(Class<? extends Annotation> paramClass, Map<String, Object> paramMap) {
return ((Annotation)Proxy.newProxyInstance(paramClass.getClassLoader(), new Class[] { paramClass }, new AnnotationInvocationHandler(paramClass, paramMap)));
}
生成的註解對象帶有註解的名稱和值——paramMap
,不管是什麼類型的註解,註解對象的equals
、toString
、hashCode
、annotationType
函數以及註解值的獲取,都會調用AnnotationInvocationHandler.invoke
,由其返回結果。
回到主題,一般修改註解值的代碼如下:
Field detailField = message.getClass().getDeclaredField("detail");
if (detailField != null) {
detailField.setAccessible(true);
JsonIgnore annotation = detailField.getAnnotation(JsonIgnore.class);
if (annotation != null) {
InvocationHandler ih = Proxy.getInvocationHandler(annotation);
Field memberValuesField = ih.getClass().getDeclaredField("memberValues");
memberValuesField.setAccessible(true);
Map memberValues = (Map)memberValuesField.get(ih);
memberValues.put("value", false); // set value to false
}
}
ih
就是註解的代理對象,memberValues
就是代理類AnnotationInvocationHandler
的私有變量,也就是註解的名稱和值paramMap
,通過修改memberValues
,從而修改註解值。
所以先了解了代理,再理解註解變得水到渠成。