代理,註解,接口和實現類的小測驗

* retention : 保留
* policy : 策略

ps : 簡單測試了一下手寫代理,jdk動態代理,和cglib動態代理,根據其不同的結果分析其原理

一:測試目的

  • 主要想看一下不同的代理模式對於目標類中方法上註解的讀取能力

二:代理簡述

  • jdk動態代理:只針對接口操作
  • cglib動態代理:既可以爲沒有實現接口的類去做代理,也可以爲實現接口的類去做代理

三:代碼準備

1)自定義註解

  • 設置保留策略爲運行期
  • 註解中只定義一個數組
  • 當屬性名爲value時,註解中可以省略不寫
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
	String[] value();
}

2)接口

public interface Service {
	void eat();
}

3)實現類

  • 在eat()方法上添加自定義註解
public class ServiceImpl implements Service {
	@Override
	@MyAnno({"jim","tom"})
	public void eat() {
		System.out.println("eat ... ");
	}
}

三:測試實例

1)手寫代理模式

public class AnnoTest implements Service{

	@MyAnno({ "jim", "tom" })
	public void test() {
		System.out.println("test...");
	}

	private Service target;

	public AnnoTest(Service target) {
		this.target = target;
	}
	
	@Override
	public void eat() {

		try {
			Class clazz = target.getClass();
			Method method = clazz.getMethod("eat");
			if(method.isAnnotationPresent(MyAnno.class)) {
				MyAnno myAnno = method.getAnnotation(MyAnno.class);
				System.out.println(myAnno.value()[0]);
				target.eat();
				System.out.println(myAnno.value()[1]);
			}else {
				target.eat();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest annoTest = new AnnoTest(service);
		annoTest.eat();
	}

}
  • 測試結果
jim
eat ... 
tom
  • 原理分析 : 這個應該比較容易理解,就是一個父類引用指向子類對象,調用方法的時候調用的是實現類的方法實現,我們調用的時候,是在代理對象的eat()方法中判斷目標類的eat()方法上有沒有我們的註解,能讀到很正常

2)JDK動態代理

public class AnnoTest2 implements InvocationHandler{

	private Service target;

	public AnnoTest2(Service target) {
		this.target = target;
	}
	
	public Object createProxy() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object ret = null;
		if(method.isAnnotationPresent(MyAnno.class)) {
			MyAnno anno = method.getAnnotation(MyAnno.class);
			System.out.println(anno.value()[0]);
			ret = method.invoke(target, args);
			System.out.println(anno.value()[1]);
		}else {
			ret = method.invoke(target, args);
		}
		return ret;
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest2 annoTest = new AnnoTest2(service);
		Service proxy = (Service) annoTest.createProxy();
		proxy.eat();
		
	}

}
  • 運行結果
eat ... 
  • 原理分析 : jdk動態代理,它代理的是接口,然後根據反射技術進行實現,通過invoke()方法我們可以知道,我們是在調用代理實例的方法,接口上並沒有我們的自定義註解,所以代理實例上也不會有註解
  • 如果在接口上加上註解,則可以讀到

3)CGLIB動態代理

public class AnnoTest3 implements MethodInterceptor {

	private Object target;

	public AnnoTest3(Object target) {
		this.target = target;
	}

	public Object createProxy() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object object, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		Object ret = null;
		if(method.isAnnotationPresent(MyAnno.class)) {
			MyAnno anno = method.getAnnotation(MyAnno.class);
			String[] val = anno.value();
			System.out.println(val[0]);
			ret = method.invoke(target, args);
			System.out.println(val[1]);
		}else {
			ret = method.invoke(target, args);
		}
		return ret;
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest3 annoTest = new AnnoTest3(service);
		Service proxy = (Service) annoTest.createProxy();
		proxy.eat();
	}

}
  • 運行結果
jim
eat ... 
tom
  • 原理分析 : cglib代理的目標對象是實現類對象,所以是可以讀到的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章