關于JDK動态代理與Cglib代理
最近有時間學習一下SpringAOP源碼,底層用到了代理,大概是這樣的:
當需要被代理的類實作了接口,則使用JDK動态代理建立代理對象,增加增強操作執行目标方法
當需要被代理的類未實作接口,則使用Cglib代理建立目标類的子類,增加增強操作執行目标方法
由此可見JDK動态代理的使用條件是 被代理的類必須實作了接口。(接口是什麼無關要緊,但是必須實作了接口,生成的代理類對象也是實作了此接口的類)
Cglib代理主要是通過增強位元組碼,生成目标代理類的子類進而實作代理。是以這裡要求目标類不能被final修飾!!!
>>JDK動态代理
1.定義動态代理類 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK動态代理類
* @author Administrator
* 被代理類需要實作一個接口
*/
public class JDKDynamicProxy implements InvocationHandler{
Object targetObj;//代理對象
/**
* 傳回代理後的對象
* @param obj 需要被代理的對象
* @return
*/
public Object getProxy(Object obj){
this.targetObj = obj;
/*
* 參數解析:
* obj.getClass().getClassLoader() 指定代理類的類加載器
* obj.getClass().getInterfaces() 需要被代理的類所實作的接口數組
* this 目前InvocationHandler對象
*/
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
/**
* 調用目标對象的方法時,會通過代理對象的invoke方法 再裡面執行目标對象的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK proxy invoke執行前");//在目标方法執行前的增強操作
method.invoke(targetObj, args);//執行目标方法
System.out.println("JDK proxy invoke執行後");//在目标方法執行後的增強操作
return null;
}
}
2.目标類的接口定義 UserService.java
package com.dfx.study.jdkdynamic;
/**
* 定義接口,用于測試JDK動态代理 生成代理對象
* @author Administrator
*
*/
public interface UserService {
public void say();
}
3.目标類定義 (實作了UserService接口) UserServiceImpl.java
package com.dfx.study.jdkdynamic;
/**
* 接口的實作類(也就是這次用于測試的需要被代理的類)
* @author Administrator
*
*/
public class UserServiceImpl implements UserService{
@Override
public void say() {
System.out.println("我是目标類真實輸出内容:Hello JDKDynamicProxy!!!");
}
}
4.測試代理類 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic;
/**
* JDK動态代理測試類
* @author Administrator
*
*/
public class JDKDynamicProxyTest {
public static void main(String[] args){
JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//建立實作InvocationHandler接口的對象 動态代理實作類對象
UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理對象
userService.say();//調用代理對象的say方法
}
}
5.測試結果:
JDK proxy invoke執行前
我是目标類真實輸出内容:Hello JDKDynamicProxy!!!
JDK proxy invoke執行後
進一步思考,判斷JDK動态代理是不是真的隻能代理實作接口的類呢,我們去掉接口試一下,也就是修改 UserServiceImpl.java如下
package com.dfx.study.jdkdynamic;
/**
* 接口的實作類(也就是這次用于測試的需要被代理的類)
* @author Administrator
*
*/
public class UserServiceImpl {
public void say() {
System.out.println("我是目标類真實輸出内容:Hello JDKDynamicProxy!!!");
}
}
更改測試類 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic;
/**
* JDK動态代理測試類
* @author Administrator
*
*/
public class JDKDynamicProxyTest {
public static void main(String[] args){
JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//建立實作InvocationHandler接口的對象 動态代理實作類對象
//UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理對象
UserServiceImpl userService = (UserServiceImpl)jdkProxy.getProxy(new UserServiceImpl());//不通實作接口 生成代理對象
userService.say();//調用代理對象的say方法
}
}
測試結果: (會抛出異常,生成的代理類不能被強制轉換為我們的目标類UserServiceImpl,是以最後生成的代理類一定是和目标類實作了同一接口的類)
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.dfx.study.jdkdynamic.UserServiceImpl
at com.dfx.study.jdkdynamic.JDKDynamicProxyTest.main(JDKDynamicProxyTest.java:13)
JDK動态代理總結:
1.代理邏輯的實際處理類一定要實作InvocationHandler(攔截器)接口,在其中的invoke()方法中對目标對象的方法進行增強,接口代理對象的所有方法都會轉發到invoke()方法處理
2.實際處理類中有一個Obeject類型的對象表示目标對象,所有實作了接口的要選擇此種增強的方法都可以使用這個實際處理類建立代理對象
3.代理對象使通過java反射機制在運作時通過Proxy.newProxyInstance動态生成的
4.JDK動态代理的目标類(也就是需要被代理類)必須要實作了接口,通過JDK動态代理生成的代理類一定是實作了目标類的接口,且代理對象在被調用的時候隻能調用接口裡的方法。
5.JDK動态代理傳回的代理對象隻能用接口類型接收,不能用目标類接收。(這裡說的比較抽象,下面用代碼解釋一下)
UserService userService = (UserService) jdkProxy.getProxy(new UserServiceImpl());//生成代理對象 正确
UserServiceImpl userService = (UserServiceImpl) jdkProxy.getProxy(new UserServiceImpl());//生成代理對象 錯誤
>>Cglib代理
1.定義代理類 CglibProxy.java
package com.dfx.study.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* Cglib實作代理
* @author Administrator
*
*/
public class CglibProxy implements MethodInterceptor{
Enhancer enhancer = new Enhancer();
/**
* 得到代理對象 (是被代理對象的子類)
* @param obj
* @return
*/
public Object getProxy(Object obj){
enhancer.setSuperclass(obj.getClass());//将需要被代理的類設定為代理類的父類
enhancer.setCallback(this);
Object object = enhancer.create();//建立被代理類的子類對象
return object;
}
/**
* 調用目标對象的方法時,會通過代理對象的invoke方法 再裡面執行目标對象的方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
//System.out.println("Cglib proxy 入參:obj="+obj);
System.out.println("Cglib proxy 入參:method="+method);
System.out.println("Cglib proxy 入參:params="+params);
System.out.println("Cglib proxy 入參:methodProxy="+methodProxy);
System.out.println("Cglib proxy invoke執行前");//在目标方法執行前的增強操作
String con = (String) methodProxy.invokeSuper(obj, params);
System.out.println("methodProxy.invokeSuper(obj, params) 執行結果:"+con);
System.out.println("Cglib proxy invoke執行後");//在目标方法執行前的增強操作
return null;
}
}
2.定義需要被代理的目标類 UserService.java (不需要實作其他接口)
package com.dfx.study.cglib;
/**
* 用于Cglib代理的測試類,不需要實作接口
* @author Administrator
*
*/
public class UserService {
public String say(){
System.out.println("我是目标類真實輸出内容:Hello CglibProxy!!!");
return "Cglib";
}
}
3.測試Cglib代理 CglibProxyTest.java
package com.dfx.study.cglib;
/**
* Cglib代理測試類
* @author Administrator
*
*/
public class CglibProxyTest {
public static void main(String[] args){
CglibProxy cglibProxy = new CglibProxy();
UserService userService = (UserService)cglibProxy.getProxy(new UserService());//得到代理對象
userService.say();
}
}
4.Cglib代理測試結果:
Cglib proxy 入參:method=public java.lang.String com.dfx.study.cglib.UserService.say()
Cglib proxy 入參:params=[Ljava.lang.Object;@5a10411
Cglib proxy 入參:methodProxy=org.springframework.cglib.proxy.MethodProxy@2ef1e4fa
Cglib proxy invoke執行前
我是目标類真實輸出内容:Hello CglibProxy!!!
methodProxy.invokeSuper(obj, params) 執行結果:Cglib
Cglib proxy invoke執行後
進一步證明一下Cglib代理的類不能被final修飾(final修飾的類無法被繼承),因為它要通過生成目标類的子類進而實作代理,接下來我們做如下修改 UserService.java
package com.dfx.study.cglib;
/**
* 用于Cglib代理的測試類,不需要實作接口
* @author Administrator
* 添加final修飾類
*/
public final class UserService {
public String say(){
System.out.println("我是目标類真實輸出内容:Hello CglibProxy!!!");
return "Cglib";
}
}
測試結果:(抛出異常 無法從final修飾的類中生成子類資訊,由此我們可得知如果想要通過Cglib成功代理,那這個類一定不能用final修飾)
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class com.dfx.study.cglib.UserService
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285)
at com.dfx.study.cglib.CglibProxy.getProxy(CglibProxy.java:25)
at com.dfx.study.cglib.CglibProxyTest.main(CglibProxyTest.java:11)
Cglib代理總結:
1.CGLIB動态代理不要求目标對象一定要實作接口;
2.代理邏輯的實際處理類要實作MethodInterceptor接口,在intercept()方法中對目标對象的方法進行增強
3.CGLIB通過Enhancer對象指定代理的目标對象,實作處理邏輯的對象,使用create()在運作期間動态得到代理對象;
綜合總結:
1.JDK動态代理要求目标對象一定要實作接口,Cglib則不用.
2.JDK動态代理通過反射機制要動态生成代理類,生成類的過程比較高效.
3.Cglib基于繼承來實作代理,代理對象實際上是目标對象的子類,它内部通過第三方類庫ASM,加載目标對象類的class檔案,修改位元組碼來生成子類,生成類的過程較低效,但生成類以後的執行很高效,可以通過将ASM生成的類進行緩存來解決生成類過程低效的問題.