背景:學習spring的AOP或者EasyMock的源碼時,需要對java的動態代理有深刻的了解
關于cglib的動態代理實現可以參考:
Java動態代理(cglib)
java中可以通過jdk中的java.lang.reflect.Proxy類來實現動態代理,但是我們需要先了解下什么是代理模式和靜態代理
1.代理模式簡介、目的,以及靜態代理簡介
代理模式構成要素通常是1個接口Subject,2個類RealSubject、Proxy,通用的類圖如下:
范例代碼如下:
- Subject接口
public interface Subject {
void play();
void upgrade(Integer level);
void rest();
}
- RealSubject類
public class RealSubject implements Subject {
@Override
public void play() {
System.out.println("the client is playing");
}
@Override
public void upgrade(Integer level) {
System.out.println("the client is upgrading");
}
@Override
public void rest() {
System.out.println("the client is resting");
}
}
- Proxy類
public class ProxySubject implements Subject {
private Subject proxied = null;
/**
* bind the proxied object
*
* @param client
*/
public ProxySubject(Subject client) {
this.proxied = client;
}
@Override
public void play() {
// do something before play
System.out.println("before the client is playing");
this.proxied.play();
System.out.println("after the client is playing");
// do something after play
}
@Override
public void upgrade(Integer level) {
// do something before upgrade
System.out.println("before the client is upgrading");
this.proxied.upgrade(level);
System.out.println("before the client is upgrading");
// do something after upgrade
}
@Override
public void rest() {
// do something before rest
System.out.println("before the client is resting");
this.proxied.rest();
System.out.println("before the client is resting");
// do something after rest
}
}
- 調用代碼:
public class TestMain {
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject(new RealSubject());
proxy.play();
proxy.rest();
proxy.upgrade(1);
}
}
- 運行結果:
代理模式就是當我們對RealSubject的行為進行增強,但是我們又不希望直接修改RealSubject的代碼而給出的一種無侵入式方案
1.代理模式講清楚了,那么什么是靜態代理?
當我們對一個RealSubject類進行強化時,寫一個代理類Proxy就可以了,這就是靜態代理。
2.為什么要有動態代理?
但是如果我們需要對成千上萬個RealSubject類進行強化時,是不可能去寫這么多代理類的,會造成項目代碼量暴增,代碼難于管理,這時候就需要動態代理了
動態代理的含義:在jvm中動態創建一個Proxy類,使用完之后,立即銷毀Proxy類,你在項目代碼中是看不到這個Proxy類的源碼的
2.如何實現動態代理?
動態代理的原理就是:在jvm的運行時動態創建一個類,并實例化
- 在java中,創建一個類的流程如下:
1.source經過編譯變成.class文件
2.java應用在啟動時,通過類加載器(ClassLoader)加載.class文件到jvm中并轉換為對應的class類實例
3.通過class類實例new一個對象
這里"class類實例"指的是public final class Class<T>的實例,詳情請見jdk源碼
動態代理希望能省略source編譯成.class文件的過程,在runtime中直接生成.class文件并通過自定義ClassLoader加載.class文件到jvm,后續的流程和正常類加載流程沒有區別
- 有哪些方法可以在運行時的代碼中生成二進制字節碼?
Java字節碼生成開源框架:JavaAssist和ASM(可以單開一文來講這2個框架是如何使用的)
JavaAssist和ASM相當于通過編寫java代碼生成class文件,當需要生成的class文件業務邏輯較復雜時,需要編寫的java代碼量較大,難于規模應用,那么我們java當前是通過什么方式實現動態代理的?
3.jdk的動態代理
jdk的動態代理實現中,相對于代理模式通用的Subject,RealSubject,Proxy,還多了一個InvocationHandler接口,它的用途是:
當Proxy需要調用RealSubject的方法時,不再直接調用,而是將方法對應的Method變量和RealSubject實例傳入InvocationHandler,由InvocationHandler來負責調用,并由InvocationHandler對方法調用進行增強
InvocationHandler的加入有什么好處?
我們思考這樣一種場景,RealSubject有20個方法,現在需要對這20個方法做同樣的強化邏輯,原本的經典代理模式是怎么做的?需要重復寫20次強化邏輯代碼
有了InvocationHandler之后,我們在Proxy的方法重寫處調用InvocationHandler的invoke方法,強化邏輯都是在invoke方法中實現
4.jdk動態代理范例代碼
- Subject和RealSubject代碼不變,只是Proxy類不用再手動去寫,而是通過Proxy.newProxyInstance()的方法去實現
- InvocationHanlder實現:
public class MyInvocationHandler implements InvocationHandler {
private RealSubject subject = null;
public MyInvocationHandler(RealSubject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before the method is invoked");
if (method.getName().equals("upgrade")) {
method.invoke(this.subject, args);
} else {
method.invoke(this.subject, null);
}
System.out.println("after the method is invoked");
return null;
}
}
- 測試代碼:
public class TestMain {
public static void main(String[] args) {
RealSubject subject = new RealSubject();
// 生成InvocationHandler實例
MyInvocationHandler h = new MyInvocationHandler(subject);
// 獲取被代理對象實現的接口列表
Class[] interfaces = subject.getClass().getInterfaces();
// 獲取被代理對象的classloader
ClassLoader classLoader = subject.getClass().getClassLoader();
Subject proxy = (Subject) Proxy.newProxyInstance(classLoader, interfaces, h);
proxy.play();
proxy.rest();
proxy.upgrade(1);
}
}
其中Proxy.newProxyInstance(classLoader,interfaces,h)實現了創建Proxy類的功能
可以看到,jdk的動態代理,是對接口中定義方法的強化,如果要直接實現對某個類的強化,而這個類又沒有實現任何接口,就需要借助cglib了
這跟newProxyInstance的實現有關系,newProxyInstance的參數是:classLoader,interfaces,h,可以理解是這個方法是基于interfaces去創建的動態代理,如果我們能夠在Proxy類中新建一個創建動態代理的方法,用realSubject作為參數,基于realSubject去創建代理類,那么我們也就在jdk中實現了對具體某個類的動態代理實現
下面的代碼是對上面測試代碼的修改,動態代理的創建完全跟RealSubject這個類沒有關系了,也可以看出來jdk的基于接口的動態代理的含義了:
public class TestMain {
public static void main(String[] args) {
Subject subject = new Subject() {
@Override
public void play() {
}
@Override
public void upgrade(Integer level) {
}
@Override
public void rest() {
}
};
// 生成InvocationHandler實例
MyInvocationHandler h = new MyInvocationHandler(subject);
// 獲取被代理對象實現的接口列表
Class[] interfaces = new Class[]{Subject.class};
// 獲取被代理對象的classloader
ClassLoader classLoader = subject.getClass().getClassLoader();
Subject proxy = (Subject) Proxy.newProxyInstance(classLoader, interfaces, h);
proxy.play();
proxy.rest();
proxy.upgrade(1);
}