老生常談設(shè)計模式之動態(tài)代理
一、動態(tài)代理概念
動態(tài)代理分為JDK動態(tài)代理和cglib動態(tài)代理兩種方式。
jdk動態(tài)代理是由Java內(nèi)部的反射機(jī)制來實(shí)現(xiàn)的,cglib動態(tài)代理底層則是借助asm來實(shí)現(xiàn)的。
總的來說,反射機(jī)制在生成類的過程中比較高效,而asm在生成類之后的相關(guān)執(zhí)行過程中比較高效(可以通過將asm生成的類進(jìn)行緩存,這樣解決asm生成類過程低效問題)。
還有一點(diǎn)必須注意:jdk動態(tài)代理的應(yīng)用前提,必須是目標(biāo)類基于統(tǒng)一的接口。如果沒有上述前提,jdk動態(tài)代理不能應(yīng)用。
由此可以看出,jdk動態(tài)代理有一定的局限性,cglib這種第三方類庫實(shí)現(xiàn)的動態(tài)代理應(yīng)用更加廣泛,且在效率上更有優(yōu)勢。
二、JDK動態(tài)代理
以下代碼使用代理模式實(shí)現(xiàn)一個大小寫字符轉(zhuǎn)換的功能。
定義接口和實(shí)現(xiàn)類:
ISomeService接口:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
/**
* 接口類
*
* @author Root
*/
public interface ISomeService {
String doFirst();
void doSecond();
}
SomeServiceImpl實(shí)現(xiàn)類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
/**
* 實(shí)現(xiàn)類
*
* @author Root
*/
public class SomeServiceImpl implements ISomeService {
@Override
public String doFirst() {
System.out.println("執(zhí)行doFirst()...");
String result = "abcde";
return result;
}
@Override
public void doSecond() {
System.out.println("執(zhí)行doSecond()...");
}
}
JDK動態(tài)代理類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
final ISomeService target = new SomeServiceImpl();
// 使用JDK的Proxy動態(tài)代理,要求目標(biāo)類和代理類必須實(shí)現(xiàn)相同的接口,因為其底層的執(zhí)行原理與靜態(tài)代理的相同
ISomeService service = (ISomeService) Proxy.newProxyInstance(
// 目標(biāo)類的類加載器
target.getClass().getClassLoader(),
// 目標(biāo)類所實(shí)現(xiàn)的所有接口
target.getClass().getInterfaces(),
new InvocationHandler() {
// proxy:代理對象
// method:目標(biāo)方法
// args:目標(biāo)方法的參數(shù)列表
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 調(diào)用目標(biāo)方法
Object result = method.invoke(target, args);
if (result != null) {
result = ((String) result).toUpperCase();
}
return result;
}
});
String result = service.doFirst();
System.out.println(result);
service.doSecond();
}
}
三、cglib動態(tài)代理
Cglib是一個優(yōu)秀的動態(tài)代理框架,它的底層使用ASM在內(nèi)存中動態(tài)的生成被代理類的子類,使用CGLIB即使代理類沒有實(shí)現(xiàn)任何接口也可以實(shí)現(xiàn)動態(tài)代理功能。CGLIB具有簡單易用,它的運(yùn)行速度要遠(yuǎn)遠(yuǎn)快于JDK的Proxy動態(tài)代理:
CGLIB的核心類:
net.sf.cglib.proxy.Enhancer – 主要的增強(qiáng)類
net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實(shí)現(xiàn)
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實(shí)現(xiàn)對源對象方法的調(diào)用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數(shù)是被代理對象,也不會出現(xiàn)死循環(huán)的問題。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回調(diào)(callback)類型,它經(jīng)常被基于代理的AOP用來實(shí)現(xiàn)攔截(intercept)方法的調(diào)用。這個接口只定義了一個方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
第一個參數(shù)是代理對像,第二和第三個參數(shù)分別是攔截的方法和方法的參數(shù)。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調(diào)用,或者使用 net.sf.cglib.proxy.MethodProxy對象調(diào)用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因為它更快。
以下程序?qū)崿F(xiàn)了大小寫轉(zhuǎn)換的功能:
實(shí)現(xiàn)類SomeService:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
/**
* 實(shí)現(xiàn)類
*
* @author Root
*/
public class SomeService {
public String doFirst() {
System.out.println("執(zhí)行doFirst()...");
String result = "abcde";
return result;
}
public void doSecond() {
System.out.println("執(zhí)行doSecond()...");
}
}
代理類MyCglibFactory:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
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 MyCglibFactory implements MethodInterceptor {
private SomeService target;
public MyCglibFactory() {
super();
target = new SomeService();
}
public SomeService myCglibCreator() {
// 創(chuàng)建增強(qiáng)器對象
Enhancer enhancer = new Enhancer();
// 指定目標(biāo)類,即父類
enhancer.setSuperclass(SomeService.class);
// 設(shè)置回調(diào)接口對象
enhancer.setCallback(this);
return (SomeService) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 調(diào)用目標(biāo)方法
Object result = method.invoke(target, args);
if (result != null) {
result = ((String) result).toUpperCase();
}
return result;
}
}
測試:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
public class Main {
public static void main(String[] args) {
SomeService service = new MyCglibFactory().myCglibCreator();
String result = service.doFirst();
System.out.println("result = " + result);
service.doSecond();
}
}
運(yùn)行結(jié)果:
執(zhí)行doFirst()... result = ABCDE 執(zhí)行doSecond()...
以上這篇老生常談設(shè)計模式之動態(tài)代理就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)表單必填參數(shù)驗證的方法
表單校驗是很多注冊時必做的功能, 一般我們的處理都是很粗暴的寫個if()判斷, 然后拋異常. 本文將介紹通過代理的思想, 用注解優(yōu)雅的處理非空判斷,感興趣的一起來了解一下2021-05-05
詳解獲取Spring MVC中所有RequestMapping以及對應(yīng)方法和參數(shù)
本篇文章主要介紹了詳解獲取Spring MVC中所有RequestMapping以及對應(yīng)方法和參數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03
java的Map集合中按value值進(jìn)行排序輸出的實(shí)例代碼
下面小編就為大家?guī)硪黄猨ava的Map集合中按value值進(jìn)行排序輸出的實(shí)例代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08

