其实早先是查到动态修改注解值的方法,然后才慢慢去了解动态代理的,但既然写文章做总结,最好是从原理开始。
一个简单例子
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
,从而修改注解值。
所以先了解了代理,再理解注解变得水到渠成。
版权声明:本文为poduoduopo原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。