從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,從而修改註解值。

所以先了解了代理,再理解註解變得水到渠成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章