代理(Proxy)是一種常用的行為型設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪(fǎng)問(wèn)方式;即通過(guò)代理對(duì)象訪(fǎng)問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。比如日志嫩絮,統(tǒng)計(jì)操作,常用框架中Mybaits中的
Mapper
就是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)方法的調(diào)用的。
如圖所示代理模式是對(duì)目標(biāo)對(duì)象的封裝和隱藏,通過(guò)關(guān)聯(lián)目標(biāo)對(duì)象,并向外暴露于目標(biāo)接口同樣的行為方法弱左,從而通過(guò)內(nèi)部完成對(duì)目標(biāo)對(duì)象的增強(qiáng)。
一炕淮、靜態(tài)代理
靜態(tài)代理要求被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類(lèi)拆火,一下以打印日志為例實(shí)現(xiàn)靜態(tài)代理
- 公共接口,定義用戶(hù)相關(guān)的業(yè)務(wù)邏輯
/**
* @author haopeng
* @date 2020-08-11 15:14
*/
public interface BizService {
void doSomething();
}
- 2.具體實(shí)現(xiàn)類(lèi)涂圆,也就是被代理的目標(biāo)對(duì)象
public class UserServiceImpl implements BizService {
@Override
public void doSomething() {
System.out.println("執(zhí)行用戶(hù)相關(guān)業(yè)務(wù)邏輯");
}
}
- 3.代理類(lèi)们镜,對(duì)目標(biāo)對(duì)象進(jìn)行增強(qiáng)
public class UserLogProxy implements BizService {
private final BizService service = new UserServiceImpl();
@Override
public void doSomething() {
System.out.println("靜態(tài)代理執(zhí)行前。润歉。模狭。打印日志 。踩衩。嚼鹉。。" );
service.doSomething();
System.out.println("靜態(tài)代理執(zhí)行后驱富。反砌。。 打印日志 萌朱。宴树。。晶疼。" );
}
}
- 4.測(cè)試
public class Client {
public static void main(String[] args) {
UserLogProxy userLogProxy = new UserLogProxy();
userLogProxy.doSomething();
}
}
程序運(yùn)行結(jié)果:
靜態(tài)代理執(zhí)行前酒贬。。翠霍。打印日志 锭吨。。寒匙。零如。
執(zhí)行用戶(hù)相關(guān)業(yè)務(wù)邏輯
靜態(tài)代理執(zhí)行后躏将。。考蕾。 打印日志 祸憋。。肖卧。蚯窥。
可以看到完成了對(duì)用戶(hù)業(yè)務(wù)方法的日志打印功能,但是動(dòng)態(tài)代理明顯的區(qū)別就是:需要再編譯器確定被代理對(duì)象塞帐,試想一下拦赠,如果還有訂單、商品等業(yè)務(wù)方法也需要打印日志的話(huà)葵姥,那么我們就需要對(duì)每一個(gè)業(yè)務(wù)類(lèi)編寫(xiě)對(duì)應(yīng)的代理類(lèi)實(shí)現(xiàn)荷鼠。
二、動(dòng)態(tài)代理
利用反射機(jī)制在運(yùn)行時(shí)創(chuàng)建代理類(lèi)榔幸。動(dòng)態(tài)代理的實(shí)現(xiàn)方式有兩種分別是
jdk動(dòng)態(tài)代理
和cglib動(dòng)態(tài)代理
1.jdk動(dòng)態(tài)代理
jdk動(dòng)態(tài)代理的實(shí)現(xiàn)步驟:
- 通過(guò)實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器允乐;
- 通過(guò)為 Proxy 類(lèi)指定 ClassLoader 對(duì)象和一組 interface 來(lái)創(chuàng)建動(dòng)態(tài)代理類(lèi);
- 通過(guò)反射機(jī)制獲得動(dòng)態(tài)代理類(lèi)的構(gòu)造函數(shù)牡辽,其唯一參數(shù)類(lèi)型是調(diào)用處理器接口類(lèi)型喳篇;
- 通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類(lèi)實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入态辛。
- 代碼實(shí)現(xiàn)
public interface UserBizService {
void doUserSomething();
}
public class UserServiceImpl implements UserBizService {
@Override
public void doUserSomething() {
System.out.println("執(zhí)行商用戶(hù)相關(guān)的業(yè)務(wù)邏輯麸澜。。奏黑。");
}
}
- 通過(guò)實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器
public class LogProxy implements InvocationHandler {
private Object object;
public LogProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("動(dòng)態(tài)代理執(zhí)行前炊邦。。熟史。打印日志");
System.out.println( "執(zhí)行方法" + method.getName());
Object invoke = method.invoke(object, args);
System.out.println("動(dòng)態(tài)代理執(zhí)行后馁害。。蹂匹。打印日志");
return invoke;
}
}
public class Client {
public static void main(String[] args) {
UserBizService hello = new UserServiceImpl();
InvocationHandler invocationHandler = new LogProxy(hello);
UserBizService userBizService = (UserBizService) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), invocationHandler);
userBizService.doUserSomething();
}
}
執(zhí)行結(jié)果
動(dòng)態(tài)代理執(zhí)行前碘菜。。限寞。打印日志
執(zhí)行方法doUserSomething
執(zhí)行商用戶(hù)相關(guān)的業(yè)務(wù)邏輯忍啸。。履植。
動(dòng)態(tài)代理執(zhí)行后计雌。。玫霎。打印日志
可以看到符合預(yù)期效果凿滤,現(xiàn)在如果想新增一個(gè)代理功能妈橄,比如為商品業(yè)務(wù)方法打印日志,我們只需要新增幾行代碼就可以搞定翁脆,不需要額外的為商品服務(wù)單獨(dú)創(chuàng)建代理類(lèi)的實(shí)現(xiàn)
- 用到的基礎(chǔ)服務(wù)類(lèi)
public interface GoodService {
public void doGoodThing();
}
public class GoodServiceImpl implements GoodService {
@Override
public void doGoodThing() {
System.out.println("執(zhí)行商品相關(guān)的業(yè)務(wù)邏輯眷蚓。。鹃祖。");
}
}
- 測(cè)試類(lèi)中通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類(lèi)實(shí)例
public class Client {
public static void main(String[] args) {
UserBizService userService = new UserServiceImpl();
InvocationHandler invocationHandler = new LogProxy(userService);
// 以下代碼執(zhí)行會(huì)直接報(bào)錯(cuò)溪椎,因?yàn)閖dk的動(dòng)態(tài)代理是能代理接口類(lèi)
//HelloInterface helloInterface = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), new Class[]{hello.getClass()}, invocationHandler);
UserBizService userBizService = (UserBizService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
userBizService.doUserSomething();
GoodService goodservice = new GoodServiceImpl();
InvocationHandler invocationHandlerBye = new LogProxy(goodservice);
GoodService goodService = (GoodService) Proxy.newProxyInstance(goodservice.getClass().getClassLoader(), goodservice.getClass().getInterfaces(), invocationHandlerBye);
goodService.doGoodThing();
}
}
- 運(yùn)行結(jié)果
動(dòng)態(tài)代理執(zhí)行前普舆。恬口。。打印日志
執(zhí)行方法doUserSomething
執(zhí)行商用戶(hù)相關(guān)的業(yè)務(wù)邏輯沼侣。祖能。。
動(dòng)態(tài)代理執(zhí)行后蛾洛。养铸。。打印日志
動(dòng)態(tài)代理執(zhí)行前轧膘。钞螟。。打印日志
執(zhí)行方法doGoodThing
執(zhí)行商品相關(guān)的業(yè)務(wù)邏輯谎碍。。。
動(dòng)態(tài)代理執(zhí)行后蹋半。若债。。打印日志
總結(jié): jdk
動(dòng)態(tài)代理要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口的目標(biāo)對(duì)象,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒(méi)有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用以目標(biāo)對(duì)象子類(lèi)的方式類(lèi)實(shí)現(xiàn)代理,這種動(dòng)態(tài)代理就叫Cglib
2.cglib動(dòng)態(tài)代理
Cglib代理,也叫作子類(lèi)代理,它是在內(nèi)存中構(gòu)建一個(gè)子類(lèi)對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.
示例代碼:
- 代理類(lèi)實(shí)現(xiàn)
MethodInterceptor
接口
public class CglibLogProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
System.out.println(method.getName());
Object o1 = methodProxy.invokeSuper(o, args);
System.out.println("++++++after " + methodProxy.getSuperName() + "++++++");
return o1;
}
}
- 業(yè)務(wù)類(lèi)(被代理類(lèi))熔任,
UserBiz
不用實(shí)現(xiàn)任何接口
public class UserBiz {
public String getInfo(String msg) {
System.out.println("=======執(zhí)行業(yè)務(wù)方法======");
System.out.println("=======方法參數(shù)為: <" + msg + ">=======");
return "=======返回業(yè)務(wù)方法執(zhí)行完畢的返回值======";
}
}
- 測(cè)試類(lèi)
public class Client {
public static void main(String[] args) {
CglibLogProxy cglibProxy = new CglibLogProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserBiz.class);
enhancer.setCallback(cglibProxy);
UserBiz o = (UserBiz)enhancer.create();
String info = o.getInfo("路漫漫其修遠(yuǎn)兮");
System.out.println(info);
}
}
- 執(zhí)行結(jié)果
++++++before CGLIB$getInfo$0++++++
getInfo
=======執(zhí)行業(yè)務(wù)方法======
=======方法參數(shù)為: <路漫漫其修遠(yuǎn)兮>=======
++++++after CGLIB$getInfo$0++++++
=======返回業(yè)務(wù)方法執(zhí)行完畢的返回值======
可以看到同樣完成了代理功能褒链,其實(shí)Spring
框架中AOP
就是根據(jù)目標(biāo)類(lèi)進(jìn)行判斷,從而決定使用哪種代理方式疑苔,如果目標(biāo)類(lèi)實(shí)現(xiàn)了接口就通過(guò)jdk
方式來(lái)代理甫匹,否則通過(guò)cglib
方式來(lái)代理。
優(yōu)化改進(jìn)
以上的寫(xiě)法可以結(jié)合工廠(chǎng)模式和泛型的思想再優(yōu)化一下惦费,這樣沒(méi)增加一個(gè)代理類(lèi)就不必寫(xiě)重復(fù)的代碼
/**
* @author haopeng
* @date 2020-08-12 10:47
*/
public class ProxyFactory implements MethodInterceptor {
@SuppressWarnings("unchecked")
public <T> T getProxyInstance(Class<?> clz) {
Enhancer en = new Enhancer();
en.setSuperclass(clz);
en.setCallback(this);
return (T)en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("方法開(kāi)始了 .....");
//Object invoke = method.invoke(target, args);
Object invoke = methodProxy.invokeSuper(obj, args);
System.out.println("方法結(jié)束了 .....");
return invoke;
}
}
- 測(cè)試
public class Client {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
UserBiz userBizProxy = proxyFactory.getProxyInstance(UserBiz.class);
userBizProxy.getInfo("好玩的cglib動(dòng)態(tài)代理");
}
}
- 結(jié)果
方法開(kāi)始了 .....
=======執(zhí)行業(yè)務(wù)方法======
=======方法參數(shù)為: <好玩的cglib動(dòng)態(tài)代理>=======
方法結(jié)束了 .....
總結(jié):
-
JDK
的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果想代理沒(méi)有實(shí)現(xiàn)接口的類(lèi),就可以使用Cglib
實(shí)現(xiàn). - Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類(lèi)與實(shí)現(xiàn)java接口.它廣泛的被許多
AOP
的框架使用,例如Spring AOP
,為他們提供方法的interception
(攔截) - Cglib包的底層是通過(guò)使用一個(gè)小而塊的字節(jié)碼處理框架
ASM
來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類(lèi).不鼓勵(lì)直接使用ASM,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉
補(bǔ)充一點(diǎn)ASM技術(shù)在SpringMVC
中也有用到兵迅,SpringMVC
中填充Controller
參數(shù)的時(shí)候獲取參數(shù)的名稱(chēng)就是通過(guò)ASM
技術(shù),因?yàn)榉瓷涫遣荒苣玫絽?shù)名的
三趁餐、Mybatis中的動(dòng)態(tài)代理
在使用
Mybatis
時(shí)我們只需要聲明Mapper
接口喷兼,但是并沒(méi)有編寫(xiě)具體的實(shí)現(xiàn)類(lèi),那么Mybaits
最終是怎樣執(zhí)行Mapper
中的方法的后雷,其實(shí)就是通過(guò)JDK
動(dòng)態(tài)代理,生成代理對(duì)象季惯。
源碼示例:
通過(guò)SqlSession
獲取UserMapper
代理對(duì)象
SqlSession session = new DefaultSqlSession(null, null, true);
session.getMapper(UserMapper.class);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 獲取代理工廠(chǎng)
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
// 通過(guò)Mapper代理工廠(chǎng)生成代理類(lèi)
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
MapperProxyFactory#newInstance
// 通過(guò)jdk動(dòng)態(tài)代理實(shí)現(xiàn)生成代理對(duì)象
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// 先生成MapperProxy代理類(lèi)
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}