JDK和CGLib動態代理
一篇寫的很好的解釋動態代理原理的文章:
博客原地址:http://www.rzrgm.cn/lifullmoon/p/14654836.html
代理
代理:在不改變原始代碼的情況下修改對象的行為。代理可以以透明的方式為對象添加額外的功能。
言簡意賅:方法增強
分類
靜態代理
人為編寫,編譯時就存在
靜態代理就是通過實現被代理對象所實現的接口,內部保存了被代理對象,在實現的方法中對處理邏輯進行增強,實際的方法執行調用了被代理對象的方法。
靜態代理實現步驟:
-
定義一個接口及其實現類;
-
創建一個代理類同樣實現這個接口
-
將目標對象【實現類】注入進代理類,然后在代理類的對應方法調用目標類中的對應方法。
這樣的話,我們就可以通過代理類屏蔽對目標對象的訪問,并且可以在目標方法執行前后做一些自己想做的事情。
代碼示例:
接口
public interface Email {
void sendEmail();
}
實現類
public class EmailImpl implements Email{
@Override
public void sendEmail() {
System.out.println("send email");
}
}
代理類創建
public class EmailProxy implements Email {
private EmailProxy() {
}
private Email email;
public EmailProxy(Email email) {
this.email = email;
}
@Override
public void sendEmail() {
System.out.println("方法開始執行");
email.sendEmail();
System.out.println("方法執行結束");
}
}
測試
public class Test {
public static void main(String[] args) {
EmailProxy emailProxy = new EmailProxy(new EmailImpl());
emailProxy.sendEmail();
}
}

缺點:
1、接口一旦新增方法,目標對象和代理對象都要進行修改
2、每個代理類只能為一個接口服務,不能為多個接口服務【針對每個目標類都單獨創建一個代理類】
? 假設還有一個Phone的接口,又要再寫一個Phone的代理類,麻煩!
3、代理類必須實現接口
動態代理
jdk動態代理
運行時生成代理類,編譯時不存在
解決了靜態代理每個代理類只能代理一個目標的問題,但是目標對象必須還是要實現接口
代碼示例:
代理類:Proxy.newProxyInstance()方法
? 參數:類加載器、目標對象實現的接口、處理接口方法調用的InvocationHandler處理器
public class JDKProxyFactory {
private JDKProxyFactory(){}
public static Object getProxy(Object target){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyMethodInterceptor(target));
}
}
處理器
public class MyMethodInterceptor implements InvocationHandler {
private final Object target;
public MyMethodInterceptor(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法開始執行");
Object res = method.invoke(target, args);
System.out.println("方法執行結束");
return res;
}
}
測試
public class Test {
public static void main(String[] args) {
Email email = (Email) JDKProxyFactory.getProxy(new EmailImpl());
email.sendEmail();
System.out.println("=================================");
Phone phon = (Phone) JDKProxyFactory.getProxy(new PhoneImpl());
phon.call();
}
}

缺點:只能代理實現了接口的類
為什么 JDK 動態代理只能基于接口代理,不能基于類代理?
在JDK動態代理的底層代碼中,對于入參中的
interfaces如果存在非接口,那么會拋出異常;且從生成的代理對象中看到會繼承Proxy這個類,在 Java 中類只能是單繼承關系,無法再繼承一個代理類,所以只能基于接口代理。
CGLIB 動態代理
【天空一聲巨響,cglib閃亮登場】
CGLIB 通過繼承方式實現代理。
CGLIB 動態代理基于類代理(字節碼提升),通過 ASM(Java 字節碼的操作和分析框架)將被代理類的 class 文件加載進來,通過修改其字節碼生成子類來處理。
很多知名的開源框架都使用到了CGLIB,例如 Spring 中的 AOP 模塊中:
如果目標對象實現了接口或者是代理類的子類,則默認采用 JDK 動態代理,否則采用 CGLIB 動態代理。

CGLIB基本介紹:
Code Generation Library代碼生成類庫,可以動態的給代理類生成一個子類,然后使用繼承方式重寫被代理方法
CGLIB基本使用
-
創建增強器對象Enhancer
-
給增強器對象設置要代理的父類以及攔截器回調
-
創建方法攔截器類,實現MethodInterceptor接口
-
創建代理對象,并調用方法
JDK 動態代理只能代理實現了接口的類或者直接代理接口,而 CGLIB 可以代理未實現任何接口的類。
代碼示例:
類
public class Email1 {
public void sendEmail()
{
System.out.println("發送郵件");
}
}
創建代理類
public class CglibProxyFactory {
private CglibProxyFactory(){}
public static Object getProxy(Class<?> clazz)
{
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(clazz.getClassLoader());
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MyCallback());
return enhancer.create();
}
}
攔截器回調Callback
因為 MethodInterceptor 繼承了 Callback 回調接口,所以可以傳入一個 MethodInterceptor 方法攔截器
注意,如果你想設置一個 Callback[] 數組去處理不同的方法,那么需要設置一個 CallbackFilter 篩選器,用于選擇這個方法使用數組中的那個 Callback 去處理
public class MyCallback implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
return result;
}
}
測試
public class Test {
public static void main(String[] args) {
Email1 email = (Email1) CglibProxyFactory.getProxy(Email1.class);
email.sendEmail();
}
}

CGLIB 動態代理在 JVM 運行時會新生成一個代理類,這個代理對象繼承了目標類,也就是創建了一個目標類的子類,從而字節碼得到提升。

浙公網安備 33010602011771號