代理模式
代理模式的定義:代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。
1、靜態代理
我們有一個接口Movable,實現該接口的類都是可移動的:
interface Movable{
void move();
}
public class Car implements Movable {
@Override
public void move() {
System.out.println("Car move...");
//模擬移動的時間,隨機10秒以內
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Bicycle implements Movable {
@Override
public void move() {
System.out.println("Bicycle move...");
//模擬移動的時間,隨機10秒以內
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
現在想要實現的功能是在不改動源碼的情況下得到移動的時間,我們只能生成一箇中間類,將Movale的類組合進去,這個類也實現Movale接口,但是move方法中在調用m之前之後完成相應的功能:
class MoveTime implements Movable{
Movable m;
public MoveTime(Movable m) {
this.m = m;
}
@Override
public void move() {
long s = System.currentTimeMillis();
m.move();
long e = System.currentTimeMillis();
System.out.println("move time: "+(e-s));
}
}
main函數,當要得到Car的move時間時,new Car,當想得到Bicycle的move時間時,new Bicycle:
public class Main {
public static void main(String[] args) {
MoveTime c = new MoveTime(new Car());
c.move();
}
}
MoveTime是代理類,Car、Bicycle都是被代理的對象。
靜態代理實現了在不改變原有代碼的情況下,完成對已有功能的增強,所謂增強,就是在完成原有功能的基礎上添加新的功能。
靜態代理的缺點是一個代理類只能實現一類接口的代理,當我們要對除Movale之外的接口類代理時,需要自己再手寫一個代理類,一類接口對應一個代理類,這樣做十分麻煩,於是就有了動態代理。
2、動態代理
動態代理是靜態代理的一種進化,靜態代理的缺點已經很明顯,動態代理解決了這個問題。
首先需要明確的是,動態代理解決的是代理類的問題,那它是怎麼實現代理類的生成呢?先看代碼:
//接口與Car類完全不動
interface Movable{
void move();
}
public class Car implements Movable {
@Override
public void move() {
System.out.println("Car move...");
//模擬移動的時間,隨機10秒以內
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
主函數調用:
public static void main(String[] args) {
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Car car = new Car();
Movable m = (Movable) Proxy.newProxyInstance
(
Car.class.getClassLoader(),
Car.class.getInterfaces(),
new MoveTimeProxy(car)
);
m.move();
}
Movable m = (Movable) Proxy.newProxyInstance(Car.class.getClassLoader(),Car.class.getInterfaces(),new MoveTimeProxy(car));
這句話是通過newProxyInstance方法生成一個代理類,其中的參數:
- 指定用哪個類加載器加載(
ClassLoader loader
) - 指定該代理類要實現哪個接口(
Class<?>[] interfaces
) - 指定調用處理器。(
InvocationHandler h
)
其中最主要的是InvocationHandler
,這個類主要實現處理過程,正如靜態代理中的計算時間等功能,都通過這個類來實現:
//聲明一個實現InvocationHandler接口的類,並重寫invoke方法。
public class MoveTimeProxy implements InvocationHandler {
Object obj;
public MoveTimeProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long s = System.currentTimeMillis();
Object result = method.invoke(obj,args);
long e = System.currentTimeMillis();
System.out.println("move time: "+(e-s));
return result;
}
}
主函數調用結果:
//Car move...
//move time: 5000
不過我們反過來再看主函數,也沒有調用MoveTimeProxy中的invoke方法啊,在哪裏調用的呢?
執行過程分析:
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
這句話是將動態代理生成的類進行保存。當執行後會發現:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1bSHwN9n-1590484407375)(C:\Users\秦\AppData\Roaming\Typora\typora-user-images\1590483472668.png)]
生成了一個$Proxy0.class的文件,這個文件就是動態代理中間生成的文件,我們通過IDEA的反編譯可以看到這個類的全貌:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package proxy.dynamicsproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Movable {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("proxy.dynamicsproxy.Movable").getMethod("move");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
這個類繼承自Proxy,實現了Movable接口。
而這個類的構造函數:
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
其中的參數是InvocationHandler,而我們自己寫的處理類也是實現了InvocationHandler這個接口,所以這裏的var1就是我們寫的MoveTime類。我們再點進去super看一眼:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
此時將我們的MoveTime類對象已經傳給了this.h。記住這裏,下面會講到。
再回來看.class文件,其中有move方法:
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
這個方法中調用的是super.h.invoke,驚不驚喜,意不意外,super.h就是我們傳入的MoveTime類對象,調用它的invoke,就是我們自己寫的那個invoke方法
所以大致流程是:當我們調用動態代理生成的類的move方法時,實則調用的是$Proxy0這個對象的的move方法,而這個方法中負責調用我們自己寫的處理函數,也就是我們自己寫的invoke方法。
而這個類是怎麼生成的呢?
採用java 的asm框架,直接對java字節碼進行操作,從而生成一個新類。