从java的动态代理到动态修改注解值

其实早先是查到动态修改注解值的方法,然后才慢慢去了解动态代理的,但既然写文章做总结,最好是从原理开始。

一个简单例子

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,不管是什么类型的注解,注解对象的equalstoStringhashCodeannotationType函数以及注解值的获取,都会调用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版权协议,转载请附上原文出处链接和本声明。