* 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代理的目標對象是實現類對象,所以是可以讀到的