一、代理模式介紹
代理模式是設(shè)計(jì)模式的一種,它提供了一種對(duì)目標(biāo)對(duì)象額外的訪問方式,即通過代理對(duì)象訪問目標(biāo)對(duì)象,這樣可以在不修改原目標(biāo)對(duì)象的前提下,提供額外的功能操作,拓展目標(biāo)對(duì)象的功能。

代理模式通常分為靜態(tài)代理和動(dòng)態(tài)代理,Spring中的AOP(面向切面編程)的實(shí)現(xiàn)就是依賴于動(dòng)態(tài)代理,動(dòng)態(tài)代理又可分為JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理。
二、靜態(tài)代理
要求:靜態(tài)代理需要代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口。
優(yōu)點(diǎn):可以在不修改目標(biāo)對(duì)象的前提下拓展目標(biāo)對(duì)象的功能。
缺點(diǎn):
- 代理類冗余:由于代理對(duì)象需要和目標(biāo)對(duì)象實(shí)現(xiàn)一致的接口,會(huì)產(chǎn)生過多的代理類。
- 不易維護(hù):一旦接口增加方法,目標(biāo)對(duì)象也需要進(jìn)行修改。
示例:
1.接口Waiter
public interface Waiter {
public void serve();
}
2.目標(biāo)對(duì)象MyWaiter
public class MyWaiter implements Waiter{
public void serve() {
System.out.println("服務(wù)了。。。");
}
}
3.代理對(duì)象WaiterPoxy
public class WaiterPoxy implements Waiter{
Waiter waiter; //代理對(duì)象的類型已經(jīng)明確為Waiter類型
public WaiterPoxy(Waiter waiter) {
super();
this.waiter = waiter;
}
public void serve() {
System.out.println("start....");
waiter.serve();
System.out.println("end....");
}
}
4.測(cè)試類TestProxy
public class TestProxy {
public static void main(String[] args) {
Waiter waiter = new WaiterPoxy(new MyWaiter());
waiter.serve();
}
}
5.輸出結(jié)果
start....
服務(wù)了。。。
end....
三、JDK動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理利用了JDK API,動(dòng)態(tài)地在內(nèi)存中構(gòu)建代理對(duì)象,從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的代理功能。動(dòng)態(tài)代理又被稱為JDK代理或接口代理。
靜態(tài)代理與動(dòng)態(tài)代理的區(qū)別主要在:
靜態(tài)代理在編譯時(shí)就已經(jīng)實(shí)現(xiàn),編譯完成后代理類是一個(gè)實(shí)際的class文件
動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)生成的,即編譯完成后沒有實(shí)際的class文件,而是在運(yùn)行時(shí)動(dòng)態(tài)生成類字節(jié)碼,并加載到JVM中
特點(diǎn):
動(dòng)態(tài)代理對(duì)象不需要實(shí)現(xiàn)接口,但是要求目標(biāo)對(duì)象必須實(shí)現(xiàn)接口,否則不能使用動(dòng)態(tài)代理。
JDK中生成代理對(duì)象主要涉及的類有:
- java.lang.reflect Proxy,主要方法為
static Object newProxyInstance(ClassLoader loader, //指定當(dāng)前目標(biāo)對(duì)象使用類加載器
Class<?>[] interfaces, //目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型
InvocationHandler h //事件處理器
)
//返回一個(gè)指定接口的代理類實(shí)例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序。
Object invoke(Object proxy, Method method, Object[] args)
// 在代理實(shí)例上處理方法調(diào)用并返回結(jié)果。
示例
1.接口類:Waiter
public interface Waiter {
public void serve();
}
2.目標(biāo)對(duì)象MyWaiter
public class MyWaiter implements Waiter{
public void serve() {
System.out.println("1號(hào)服務(wù)了。。。。。。。。。。。。。");
}
}
3.動(dòng)態(tài)代理對(duì)象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestProxy {
public static void main(String[] args) {
ClassLoader loader = TestProxy.class.getClassLoader(); //類加載器
Class[] cs = {Waiter.class}; //等待代理的類的接口
Waiter target = new MyWaiter(); //待代理對(duì)象
MyInvocationHandler h = new MyInvocationHandler(target);//調(diào)用處理器
Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
waiter.serve();
//以內(nèi)部類的形式縮寫成以下形式
// Proxy.newProxyInstance(TestProxy.class.getClassLoader(), new Class[]{Waiter.class}, new InvocationHandler() {
//
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("Hello!"); //增強(qiáng)內(nèi)容
// Object result = method.invoke(target1, args); //調(diào)用對(duì)象的方法
// System.out.println("Bye!"); //增強(qiáng)內(nèi)容
// return result;
// }
// });
}
}
/**
* 調(diào)用處理器,定義對(duì)代理對(duì)象的增強(qiáng)內(nèi)容
* @author 86156
*
*/
class MyInvocationHandler implements InvocationHandler{
private Object target; //待代理對(duì)象為Object類型,表示可以代理所有的類
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Hello!"); //增強(qiáng)內(nèi)容
Object result = method.invoke(target, args); //調(diào)用對(duì)象的方法
System.out.println("Bye!"); //增強(qiáng)內(nèi)容
return result;
}
}
四、cglib動(dòng)態(tài)代理
cglib (Code Generation Library )是一個(gè)第三方代碼生成類庫,運(yùn)行時(shí)在內(nèi)存中動(dòng)態(tài)生成一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展。
cglib特點(diǎn)
- JDK的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口。
如果想代理沒有實(shí)現(xiàn)接口的類,就可以使用CGLIB實(shí)現(xiàn)。 - CGLIB是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口。
它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。 - CGLIB包的底層是通過使用一個(gè)小而快的字節(jié)碼處理框架ASM,來轉(zhuǎn)換字節(jié)碼并生成新的類。
不鼓勵(lì)直接使用ASM,因?yàn)樗枰銓?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉。
cglib與動(dòng)態(tài)代理最大的區(qū)別就是
- 使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口
- 使用cglib代理的對(duì)象則無需實(shí)現(xiàn)接口,達(dá)到代理類無侵入。
使用cglib需要引入cglib的jar包,如果你已經(jīng)有spring-core的jar包,則無需引入,因?yàn)閟pring中包含了cglib。
- cglib的Maven坐標(biāo)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
- 示例:保存用戶功能的動(dòng)態(tài)代理實(shí)現(xiàn)
1.目標(biāo)對(duì)象:UserDao
public class UserDao{
public void save() {
System.out.println("保存數(shù)據(jù)");
}
}
2.代理對(duì)象:ProxyFactory
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory implements MethodInterceptor{
private Object target;//維護(hù)一個(gè)目標(biāo)對(duì)象
public ProxyFactory(Object target) {
this.target = target;
}
//為目標(biāo)對(duì)象生成代理對(duì)象
public Object getProxyInstance() {
//工具類
Enhancer en = new Enhancer();
//設(shè)置父類
en.setSuperclass(target.getClass());
//設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//創(chuàng)建子類對(duì)象代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開啟事務(wù)");
// 執(zhí)行目標(biāo)對(duì)象的方法
Object returnValue = method.invoke(target, args);
System.out.println("關(guān)閉事務(wù)");
return null;
}
}
3.測(cè)試類:TestProxy
import org.junit.Test;
public class TestProxy {
@Test
public void testCglibProxy(){
//目標(biāo)對(duì)象
UserDao target = new UserDao();
System.out.println(target.getClass());
//代理對(duì)象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
//執(zhí)行代理對(duì)象方法
proxy.save();
}
}
4.輸出結(jié)果
class com.cglib.UserDao
class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6
開啟事務(wù)
保存數(shù)據(jù)
關(guān)閉事務(wù)
五、總結(jié)
- 靜態(tài)代理實(shí)現(xiàn)較簡(jiǎn)單,只要代理對(duì)象對(duì)目標(biāo)對(duì)象進(jìn)行包裝,即可實(shí)現(xiàn)增強(qiáng)功能,但靜態(tài)代理只能為一個(gè)目標(biāo)對(duì)象服務(wù),如果目標(biāo)對(duì)象過多,則會(huì)產(chǎn)生很多代理類。
- JDK動(dòng)態(tài)代理需要目標(biāo)對(duì)象實(shí)現(xiàn)業(yè)務(wù)接口,代理類只需實(shí)現(xiàn)InvocationHandler接口。
- 動(dòng)態(tài)代理生成的類為 lass com.sun.proxy.$Proxy4,cglib代理生成的類為class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6。
- 靜態(tài)代理在編譯時(shí)產(chǎn)生class字節(jié)碼文件,可以直接使用,效率高。
- 動(dòng)態(tài)代理必須實(shí)現(xiàn)InvocationHandler接口,通過反射代理方法,比較消耗系統(tǒng)性能,但可以減少代理類的數(shù)量,使用更靈活。
- cglib代理無需實(shí)現(xiàn)接口,通過生成類字節(jié)碼實(shí)現(xiàn)代理,比反射稍快,不存在性能問題,但cglib會(huì)繼承目標(biāo)對(duì)象,需要重寫方法,所以目標(biāo)對(duì)象不能為final類。