前言
首先,我們要對(duì)代理(Proxy)是什么有一個(gè)準(zhǔn)確的認(rèn)識(shí),代理是一種設(shè)計(jì)模式,通俗的說(shuō),就是為目標(biāo)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)目標(biāo)對(duì)象的引用。其目的有兩個(gè):
一、通過(guò)引入代理對(duì)象的方式來(lái)間接訪問(wèn)目標(biāo)對(duì)象,防止直接訪問(wèn)目標(biāo)對(duì)象給系統(tǒng)帶來(lái)不必要的復(fù)雜性。
二、通過(guò)代理對(duì)象在不改變目標(biāo)對(duì)象的基礎(chǔ)上對(duì)目標(biāo)對(duì)象進(jìn)行增強(qiáng)(業(yè)務(wù)邏輯或功能)。
代理模式
代理模式的UML類圖如下:

可以看出,代理模式的特點(diǎn)是,目標(biāo)對(duì)象和代理對(duì)象均實(shí)現(xiàn)了同一個(gè)接口,且代理對(duì)象內(nèi)關(guān)聯(lián)了一個(gè)目標(biāo)對(duì)象。在調(diào)用時(shí),實(shí)際的執(zhí)行者為代理對(duì)象,代理對(duì)象在執(zhí)行時(shí)回調(diào)用目標(biāo)對(duì)象的方法并對(duì)其進(jìn)行增強(qiáng),下面通過(guò)靜態(tài)代理模式進(jìn)行詳細(xì)說(shuō)明。
靜態(tài)代理模式
這里,我們就以生活中的租房場(chǎng)景為例介紹靜態(tài)代理模式。租房時(shí),我們可以自己租房,也可以通過(guò)房屋中介租房,代碼如下:
租房者公共接口
package com.cube.proxy.jt;
/**
* @author cube.li
* @date 2021/3/6 14:25
* @description 接口
*/
public interface Person {
/**
* 租房
*/
void rentHouse();
}
承租人實(shí)現(xiàn)類
package com.cube.proxy.jt;
/**
* @author cube.li
* @date 2021/3/6 14:28
* @description 租房者
*/
public class Tenant implements Person {
private String name;
public Tenant(String name) {
this.name = name;
}
@Override
public void rentHouse() {
System.out.println(this.name + " 正在租房中...");
}
}
房屋中介實(shí)現(xiàn)類
package com.cube.proxy.jt;
/**
* @author cube.li
* @date 2021/3/6 14:32
* @description 中介
*/
public class Intermediary implements Person {
private String name;
/**
* 租房者
*/
private Person tenant;
public Intermediary(String name, Person tenant) {
this.name = name;
this.tenant = tenant;
}
@Override
public void rentHouse() {
System.out.println("中介 " + this.name + "確定租房預(yù)算,帶領(lǐng)查看房源...");
tenant.rentHouse();
System.out.println("中介 " + this.name + "帶領(lǐng)客戶租房完成,合同簽訂完成,收取傭金,準(zhǔn)備跑路...");
}
}
調(diào)用示例
package com.cube.proxy.jt;
/**
* @author cube.li
* @date 2021/3/6 14:32
* @description 中介
*/
public class Intermediary implements Person {
private String name;
/**
* 租房者
*/
private Person tenant;
public Intermediary(String name, Person tenant) {
this.name = name;
this.tenant = tenant;
}
@Override
public void rentHouse() {
System.out.println("中介 " + this.name + "確定租房預(yù)算,帶領(lǐng)查看房源...");
tenant.rentHouse();
System.out.println("中介 " + this.name + "帶領(lǐng)客戶租房完成,合同簽訂完成,收取傭金,準(zhǔn)備跑路...");
}
}
調(diào)用結(jié)果如下:
中介 王五確定租房預(yù)算,帶領(lǐng)查看房源...
李四 正在租房中...
中介 王五帶領(lǐng)客戶租房完成,合同簽訂完成,收取傭金,準(zhǔn)備跑路...
-----------------
中介 陳大確定租房預(yù)算,帶領(lǐng)查看房源...
張三 正在租房中...
中介 陳大帶領(lǐng)客戶租房完成,合同簽訂完成,收取傭金,準(zhǔn)備跑路...
由上面代碼可以看出,代理對(duì)象(中介)對(duì)目標(biāo)對(duì)象(承租者)在租房(業(yè)務(wù)邏輯)這一過(guò)程中對(duì)其進(jìn)行了增強(qiáng)??梢酝ㄟ^(guò)這一個(gè)示例對(duì)代理模式有一個(gè)更深刻的認(rèn)識(shí),代理過(guò)程并不是完全的代理(替代),不可能出現(xiàn)承租者(目標(biāo)對(duì)象)完全不參與租房(業(yè)務(wù)邏輯)過(guò)程,完全交由房屋中介(代理對(duì)象),這樣會(huì)出現(xiàn)房屋中介自己租了房自己去住將承租者完全撇開,顯然是不合適的;這也是為什么代理對(duì)象必須要關(guān)聯(lián)一個(gè)目標(biāo)對(duì)象的原因,代理對(duì)象只是對(duì)目標(biāo)對(duì)象進(jìn)行增強(qiáng),并不能將其與目標(biāo)對(duì)象完全隔離。
并且,還可以看出,靜態(tài)代理模式的一個(gè)缺點(diǎn)是:必須要手動(dòng)為每一個(gè)目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象,在大量使用代理模式的業(yè)務(wù)場(chǎng)景下,靜態(tài)代理模式顯然不是最合適的選擇。
AOP
AOP(Aspect Orient Programming),也即面向切面編程,作為面向?qū)ο缶幊痰囊环N補(bǔ)充,是一種成熟的編程方式。AOP與OOP互為補(bǔ)充,面向?qū)ο缶幊虒⒊绦蚍纸獬筛鱾€(gè)層次的對(duì)象,而面向切面編程將程序運(yùn)行過(guò)程分解成各個(gè)切面,可以這樣理解:面向?qū)ο缶幊淌菑撵o態(tài)角度考慮程序結(jié)構(gòu),而面向切面編程則是從動(dòng)態(tài)角度考慮程序運(yùn)行過(guò)程。
AOP實(shí)現(xiàn)可以分為兩類(按AOP框架修改源碼的時(shí)機(jī))
一、靜態(tài)AOP實(shí)現(xiàn):AOP框架在編譯階段對(duì)程序進(jìn)行修改,即實(shí)現(xiàn)對(duì)目標(biāo)類的增強(qiáng),生成靜態(tài)的AOP代理類(生成的.class文件被修改了,需要特定的編輯器),以AspectJ為代表。
二、動(dòng)態(tài)AOP實(shí)現(xiàn):AOP框架在運(yùn)行階段生成AOP代理(在內(nèi)存中以JDK動(dòng)態(tài)代理或cglib動(dòng)態(tài)生成AOP代理類),以實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的增強(qiáng),以Spring AOP為代表。
AOP相關(guān)的概念A(yù)spect、Joinpoint、Advice、Pointcut這里就不作展開了。
Spring 動(dòng)態(tài)代理
Spring默認(rèn)使用Jdk動(dòng)態(tài)代理,也可以使用cglib代理,在需要代理類而不是接口的時(shí)候,Spring會(huì)自動(dòng)切換為cglib代理。
1.Jdk動(dòng)態(tài)代理
Jdk動(dòng)態(tài)代理借助Proxy類和InvocationHandler接口實(shí)現(xiàn),Proxy用來(lái)創(chuàng)建代理對(duì)象,每一個(gè)代理對(duì)象都會(huì)關(guān)聯(lián)一個(gè)InvocationHandler實(shí)例(實(shí)現(xiàn)接口),代理對(duì)象調(diào)用方法時(shí),此次調(diào)用會(huì)被指派給其關(guān)聯(lián)的InvocationHandler實(shí)例由其執(zhí)行。
使用jdk動(dòng)態(tài)代理的代碼如下:
實(shí)現(xiàn)InvocationHandler接口
package com.cube.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author cube.li
* @date 2021/3/6 18:24
* @description
*/
public class RentInvocationHandler implements InvocationHandler {
/**
* 被代理的目標(biāo)對(duì)象
*/
private Object target;
public RentInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("租房前預(yù)處理工作...");
Object returnValue = method.invoke(target, args);
System.out.println("租房后善后工作...");
return returnValue;
}
}
創(chuàng)建代理對(duì)象調(diào)用
package com.cube.proxy.jdk;
import com.cube.proxy.stat.Person;
import com.cube.proxy.stat.Tenant;
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author cube.li
* @date 2021/3/6 18:27
* @description jdk動(dòng)態(tài)代理
*/
public class JdkProxyClient {
public static void main(String[] args) {
//目標(biāo)對(duì)象
Person target = new Tenant("李四");
//InvocationHandler
InvocationHandler handler = new RentInvocationHandler(target);
//獲取代理對(duì)象
Person proxyInstance = (Person) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
//通過(guò)代理對(duì)象調(diào)用
proxyInstance.rentHouse();
printProxyClass();
}
/**
* 將jdk動(dòng)態(tài)代理的.class文件輸出
*/
private static void printProxyClass() {
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{Person.class});
try {
String pathDir = "E:\\";
String path = "$Proxy0.class";
File f = new File(pathDir);
if (!f.exists()) {
f.mkdir();
}
path = f.getAbsolutePath() + path;
f = new File(path);
if (f.exists()) {
f.delete();
}
f.createNewFile();
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(bytes);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
調(diào)用結(jié)果如下:
租房前預(yù)處理工作...
李四 正在租房中...
租房后善后工作...
為了對(duì)Jdk動(dòng)態(tài)代理機(jī)制一窺究竟,在debug模式下查看下代理對(duì)象的類型

發(fā)現(xiàn)代理對(duì)象proxyInstance的類型是$Proxy0,將其對(duì)應(yīng)的class文件輸出并反編譯查看,其源碼如下:
import com.cube.proxy.stat.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void rentHouse() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.cube.proxy.stat.Person").getMethod("rentHouse", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
可以看出$Proxy0這個(gè)類繼承了Proxy類、實(shí)現(xiàn)了Person接口,并且其中的rentHouse()方法內(nèi)調(diào)用了與其關(guān)聯(lián)的InvocationHandler實(shí)例的invoke()方法;由于java不支持多繼承,經(jīng)Jdk動(dòng)態(tài)代理的代理對(duì)象已經(jīng)繼承了Proxy類,因此,Jdk動(dòng)態(tài)代理只能對(duì)接口進(jìn)行代理而無(wú)法再對(duì)類進(jìn)行代理。
實(shí)際上,由Jdk動(dòng)態(tài)代理生成的代理對(duì)象的類型都是以$Proxy開頭,后面的數(shù)字自零開始遞增,每生成一個(gè)代理對(duì)象該數(shù)字加一。
2.cglib動(dòng)態(tài)代理
cglib動(dòng)態(tài)代理通過(guò)MethodInterceptor接口實(shí)現(xiàn),其定義如下:
package net.sf.cglib.proxy;
/**
* General-purpose {@link Enhancer} callback which provides for "around advice".
* @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
* @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
*/
public interface MethodInterceptor
extends Callback
{
/**
* 所有生成的代理方法都會(huì)調(diào)用這個(gè)方法,而不是原始方法.
* 原始方法可以通過(guò)反射使用Method對(duì)象調(diào)用,也可以使用MethodProxy調(diào)用.
*
* @param obj 被增強(qiáng)的對(duì)象,目標(biāo)對(duì)象
* @param method 被攔截的方法
* @param args 參數(shù)
* @param proxy 用戶調(diào)用父類未被攔截的方法,根據(jù)需要可多次調(diào)用
* @return 被代理方法的返回值, 如果是void則忽略
* @throws Throwable
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
示例代碼如下:
奔跑者類
package com.cube.proxy.cglib;
/**
* @author cube.li
* @date 2021/3/6 20:34
* @description 奔跑者
*/
public class Runner {
private String name;
public Runner() {
}
public Runner(String name) {
this.name = name;
}
public void run() {
System.out.println(this.name + " 跑的飛快...");
}
}
實(shí)現(xiàn)MethodInterceptor
package com.cube.proxy.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author cube.li
* @date 2021/3/6 20:37
* @description
*/
public class RunnerMethodInterceptor implements MethodInterceptor {
/**
* 目標(biāo)對(duì)象
*/
private Object target;
public RunnerMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("踢兩腳 加速...");
Object returnVal = method.invoke(target, objects);
System.out.println("拽兩下 減速...");
return returnVal;
}
}
創(chuàng)建代理對(duì)象并調(diào)用
package com.cube.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
/**
* @author cube.li
* @date 2021/3/6 20:33
* @description
*/
public class CglibProxyClient {
public static void main(String[] args) {
//目標(biāo)對(duì)象
Runner runner = new Runner("李四");
//代理對(duì)象
RunnerMethodInterceptor interceptor = new RunnerMethodInterceptor(runner);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(runner.getClass());
//設(shè)置回調(diào)
enhancer.setCallback(interceptor);
Runner proxyInstance = (Runner) enhancer.create();
proxyInstance.run();
}
}
輸出結(jié)果
踢兩腳 加速...
李四 跑的飛快...
拽兩下 減速...
在示例中,cglib對(duì)Runner類進(jìn)行了動(dòng)態(tài)代理,增強(qiáng)其原有業(yè)務(wù)邏輯,因此cglib是對(duì)jdk動(dòng)態(tài)代理的補(bǔ)充,Spring在被代理對(duì)象沒(méi)有實(shí)現(xiàn)接口時(shí)采用cglib動(dòng)態(tài)代理替代jdk動(dòng)態(tài)代理。
綜上,本文對(duì)動(dòng)態(tài)代理進(jìn)行了淺顯的說(shuō)明,示例代碼見(jiàn)https://gitee.com/li-cube/share.git
以后有時(shí)間再分別對(duì)Jdk動(dòng)態(tài)代理、cglib動(dòng)態(tài)代理原理進(jìn)行剖析。