代理模式
在代理模式(Proxy Pattern)中,一個(gè)類(lèi)代表另一個(gè)類(lèi)的功能。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,在代理模式中,我們創(chuàng)建具有現(xiàn)有對(duì)象的對(duì)象,以便向外界提供功能接口。
意圖:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)。
主要解決:在直接訪(fǎng)問(wèn)對(duì)象時(shí)帶來(lái)的問(wèn)題,比如說(shuō):要訪(fǎng)問(wèn)的對(duì)象在遠(yuǎn)程的機(jī)器上。在面向?qū)ο笙到y(tǒng)中,有些對(duì)象由于某些原因(比如對(duì)象創(chuàng)建開(kāi)銷(xiāo)很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪(fǎng)問(wèn)),直接訪(fǎng)問(wèn)會(huì)給使用者或者系統(tǒng)結(jié)構(gòu)帶來(lái)很多麻煩,我們可以在訪(fǎng)問(wèn)此對(duì)象時(shí)加上一個(gè)對(duì)此對(duì)象的訪(fǎng)問(wèn)層。
何時(shí)使用:想在訪(fǎng)問(wèn)一個(gè)類(lèi)時(shí)做一些控制。
如何解決:增加中間層。
關(guān)鍵代碼:實(shí)現(xiàn)與被代理類(lèi)組合。
兩種機(jī)制
- 靜態(tài)代理
- 動(dòng)態(tài)代理?
靜態(tài)代理
定義:代理和被代理對(duì)象在代理之前是確定的,他們都實(shí)現(xiàn)相同的接口或者繼承相同的抽象類(lèi)
兩種實(shí)現(xiàn)機(jī)制
- 繼承
- 聚合
模式:
靜態(tài)代理的優(yōu)點(diǎn)和缺點(diǎn)
- 優(yōu)點(diǎn): 擴(kuò)展原功能,不侵入原代碼。
- 缺點(diǎn):如果通過(guò)繼承方式實(shí)現(xiàn),因?yàn)镴AVA類(lèi)只能繼承一個(gè)父類(lèi),所以需要有多個(gè)代理類(lèi);如果通過(guò)聚合實(shí)現(xiàn),當(dāng)要實(shí)現(xiàn)多個(gè)接口代理時(shí),代碼會(huì)比較臃腫,需要為每個(gè)代理接口編寫(xiě)實(shí)現(xiàn)代碼。
動(dòng)態(tài)代理
- JDK動(dòng)態(tài)代理
- CGLib動(dòng)態(tài)代理
CGLib
CGLIB(Code Generation Library)是一個(gè)開(kāi)源項(xiàng)目,是一個(gè)強(qiáng)大的,高性能,高質(zhì)量的Code生成類(lèi)庫(kù),它可以在運(yùn)行期擴(kuò)展Java類(lèi)與實(shí)現(xiàn)Java接口。Hibernate用它來(lái)實(shí)現(xiàn)PO(Persistent Object 持久化對(duì)象)字節(jié)碼的動(dòng)態(tài)生成;
jdk和cglib動(dòng)態(tài)代理實(shí)現(xiàn)的區(qū)別
- CGLib創(chuàng)建的動(dòng)態(tài)代理對(duì)象性能比JDK創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能高不少,但是CGLib在創(chuàng)建代理對(duì)象時(shí)所花費(fèi)的時(shí)間卻比JDK多得多,所以對(duì)于單例的對(duì)象,因?yàn)闊o(wú)需頻繁創(chuàng)建對(duì)象,用CGLib合適,反之,使用JDK方式要更為合適一些;
- JDK動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口;
- cglib動(dòng)態(tài)代理中生成的字節(jié)碼更加復(fù)雜,生成的代理類(lèi)是委托類(lèi)的子類(lèi),且不能處理被final關(guān)鍵字修飾的方法;
- jdk采用反射機(jī)制調(diào)用委托類(lèi)的方法,cglib采用類(lèi)似索引的方式直接調(diào)用委托類(lèi)方法;
JDK動(dòng)態(tài)代理
核心思想:通過(guò)實(shí)現(xiàn)被代理類(lèi)的所有接口,生成一個(gè)字節(jié)碼文件后構(gòu)造一個(gè)代理對(duì)象,通過(guò)持有反射構(gòu)造被代理類(lèi)的一個(gè)實(shí)例,再通過(guò)invoke反射調(diào)用被代理類(lèi)實(shí)例的方法,來(lái)實(shí)現(xiàn)代理。
缺點(diǎn):JDK動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口
知識(shí)點(diǎn)
- JDK實(shí)現(xiàn)動(dòng)態(tài)代理需要實(shí)現(xiàn)類(lèi)通過(guò)接口定義業(yè)務(wù)方法
- JDK生成的代理類(lèi)以"$Proxy"為開(kāi)頭進(jìn)行命名
- JDK代理生成的代理類(lèi)的Method在static靜態(tài)代碼塊中進(jìn)行初始化;
- public接口生成的代理類(lèi)package為"com.sun.proxy";
- JDK動(dòng)態(tài)代理需要實(shí)現(xiàn)InvocationHandler接口;
代碼:
package com.quancheng;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy {
public static void main(String[] args) {
Student stu = new Student();
CusInvocationHandler handler = new CusInvocationHandler(stu);
Play instance = (Play) Proxy.newProxyInstance(stu.getClass().getClassLoader(), stu.getClass().getInterfaces(), handler);
instance.play();
}
}
class CusInvocationHandler implements InvocationHandler {
private Object target;
public CusInvocationHandler(Object object) {
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("代理方法處理");
return method.invoke(target, args);
}
}
interface Play {
void play();
}
class Student implements Play {
@Override
public void play() {
System.err.println("student ====>");
}
}
生成的代理類(lèi)示例:
import dynamic.proxy.UserService;
import java.lang.reflect.*;
public final class $Proxy11 extends Proxy
implements UserService
{
// 構(gòu)造方法,參數(shù)就是剛才傳過(guò)來(lái)的MyInvocationHandler類(lèi)的實(shí)例
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
// 省略
}
/**
* 被代理的方法
*/
public final void add()
{
try
{
// 實(shí)際上就是調(diào)用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二個(gè)問(wèn)題就解決了
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
// 省略
}
public final String toString()
{
// 省略
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
// 在靜態(tài)代碼塊中獲取了4個(gè)方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
cglib動(dòng)態(tài)代理
核心思想:CGLib采用了非常底層的字節(jié)碼技術(shù),其原理是通過(guò)字節(jié)碼技術(shù)為一個(gè)類(lèi)創(chuàng)建子類(lèi)(CGLib底層是通過(guò)繼承實(shí)現(xiàn)的動(dòng)態(tài)代理),并在子類(lèi)中采用方法攔截的技術(shù)攔截所有父類(lèi)方法的調(diào)用,順勢(shì)織入橫切邏輯。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ);底層:使用一個(gè)小而快的字節(jié)碼處理框架ASM(Java字節(jié)碼操控框架),來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類(lèi)
缺點(diǎn):不能代理final修飾的類(lèi)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-635388.html
示例代碼:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-635388.html
//被代理的類(lèi)即目標(biāo)對(duì)象
public class A {
public void execute(){
System.out.println("執(zhí)行A的execute方法...");
}
}
//代理類(lèi)
public class CGLibProxy implements MethodInterceptor {
/**
* 被代理的目標(biāo)類(lèi)
*/
private A target;
public CGLibProxy(A target) {
super();
this.target = target;
}
/**
* 創(chuàng)建代理對(duì)象
* @return
*/
public A createProxy(){
// 使用CGLIB生成代理:
// 1.聲明增強(qiáng)類(lèi)實(shí)例,用于生產(chǎn)代理類(lèi)
Enhancer enhancer = new Enhancer();
// 2.設(shè)置被代理類(lèi)字節(jié)碼,CGLIB根據(jù)字節(jié)碼生成被代理類(lèi)的子類(lèi)
enhancer.setSuperclass(target.getClass());
// 3.//設(shè)置回調(diào)函數(shù),即一個(gè)方法攔截
enhancer.setCallback(this);
// 4.創(chuàng)建代理:
return (A) enhancer.create();
}
/**
* 回調(diào)函數(shù)
* @param proxy 代理對(duì)象
* @param method 委托類(lèi)方法
* @param args 方法參數(shù)
* @param methodProxy 每個(gè)被代理的方法都對(duì)應(yīng)一個(gè)MethodProxy對(duì)象,
* methodProxy.invokeSuper方法最終調(diào)用委托類(lèi)(目標(biāo)類(lèi))的原始方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//過(guò)濾不需要該業(yè)務(wù)的方法
if("execute".equals(method.getName())) {
//調(diào)用前驗(yàn)證權(quán)限(動(dòng)態(tài)添加其他要執(zhí)行業(yè)務(wù))
AuthCheck.authCheck();
//調(diào)用目標(biāo)對(duì)象的方法(執(zhí)行A對(duì)象即被代理對(duì)象的execute方法)
Object result = methodProxy.invokeSuper(proxy, args);
//記錄日志數(shù)據(jù)(動(dòng)態(tài)添加其他要執(zhí)行業(yè)務(wù))
Report.recordLog();
return result;
}else if("delete".equals(method.getName())){
//.....
return methodProxy.invokeSuper(proxy, args);
}
//如果不需要增強(qiáng)直接執(zhí)行原方法
return methodProxy.invokeSuper(proxy, args);
}
}
到了這里,關(guān)于代理設(shè)計(jì)模式——靜態(tài)代理和動(dòng)態(tài)代理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!