Java結構型設計模式中代理模式示例詳解
代理模式
代理模式(Proxy Pattern)屬于結構型模式。
它是指為其他對象提供一種代理以控制對這個對象的訪問。
在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
在代理模式中,創(chuàng)建具有現(xiàn)有對象的對象,以便向外界提供功能接口。
想在訪問一個類時做一些控制的時候就可以是用代理模式。
分類
代理模式屬于結構型模式,分為靜態(tài)代理和動態(tài)代理。
1.靜態(tài)代理:靜態(tài)定義代理類
靜態(tài)代理需要自己生成代理類
2.動態(tài)代理:動態(tài)生成代理類
動態(tài)代理不用親自去實現(xiàn),通常使用現(xiàn)成的API即可。目前普遍使用的是JDK自帶的代理與CGLIB提供的類庫。
主要角色
代理模式一般包含三種角色:
1.抽象主題角色(Subject)
抽象主題類的主要職責是聲明真實主題與代理的共同接口方法,該類可以是接口也可以是抽象類
2.真實主題角色(RealSubject)
該類也被稱為被代理類,該類定義了代理所表示的真實對象,是負責執(zhí)行系統(tǒng)真正的羅輯業(yè)務對象
3.代理主題角色(Proxy)
代理主題也被稱為代理類,其內(nèi)部特有RealSubject的引用,因此具備完全的對RealSubject的代理權。
客戶端調用代理對象的方法,同時也調用被代理對象的方法,但是會在代理對象前后增加一些處理代碼??梢岳斫鉃榇a增強,實際上就是在原代碼羅輯前后增加一些代碼邏輯,而使調用者無感知。
作用
1.保護目標對象,將代理對象與真實被調用目標對象分離
2.增強目標對象
3.降低系統(tǒng)耦合性,提升擴展性
靜態(tài)代理與動態(tài)代理的區(qū)別
1.靜態(tài)代理只能通過手動完成代理操作,如果被代理類增加了新的方法,代理類需要同步增加,違背開閉原則。
2.動態(tài)代理采用在運行時動態(tài)生成代碼的方式,取消了對被代理類的擴展限制,遵循開閉原測。
3.若動態(tài)代理要對目標類的增強邏輯進行擴展,結合策略模式,只需要新增策略類便可完成,無須修改代理類的代碼。
靜態(tài)代理的基本使用
靜態(tài)代理需要自己生成代理類
創(chuàng)建抽象主題
public interface ISubject {
/**
* 買票
*/
void buyTickets();
}
創(chuàng)建真實主題
public class RealSubject implements ISubject {
public void buyTickets() {
System.out.println("進行買票操作");
}
}
創(chuàng)建代理主題
public class Proxy implements ISubject {
private ISubject subject;
public Proxy(ISubject subject) {
this.subject = subject;
}
public void buyTickets() {
before();
subject.buyTickets();
after();
}
public void before() {
System.out.println("買票前的操作");
}
public void after() {
System.out.println("買票后的操作");
}
}
客戶端調用
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.buyTickets();
}
買票前的操作
進行買票操作
買票后的操作
JDK動態(tài)代理的基本使用
創(chuàng)建抽象主題
public interface IUser {
/**
* 購物
*/
void shopping();
}
創(chuàng)建真實主題
public class User implements IUser{
@Override
public void shopping() {
System.out.println("user shopping....");
}
}
創(chuàng)建代理主題
public class JDKProxy implements InvocationHandler {
private Object tarjet;
public JDKProxy(Object tarjet) {
this.tarjet = tarjet;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理user,執(zhí)行shopping()開始...");
Object oj = method.invoke(tarjet, args);
System.out.println("代理user,執(zhí)行shopping()結束...");
return oj;
}
}客戶端調用
public static void main(String[] args) {
User user = new User();
JDKProxy jdkProxy = new JDKProxy(user);
IUser proxyInstance = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), jdkProxy);
proxyInstance.shopping();
}
代理user,執(zhí)行shopping()開始...
user shopping....
代理user,執(zhí)行shopping()結束...
小優(yōu)化
在調用時候,傳入了一推參數(shù),可進一步優(yōu)化
public class JDKProxy implements InvocationHandler {
private Object tarjet;
public Object JDKProxy(Object target){
this.tarjet = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理user,執(zhí)行shopping()開始...");
Object oj = method.invoke(tarjet, args);
System.out.println("代理user,執(zhí)行shopping()結束...");
return oj;
}
} public static void main(String[] args) {
JDKProxy jdkProxy = new JDKProxy();
IUser user= (IUser)jdkProxy.JDKProxy(new User());
user.shopping();
}CGLIB動態(tài)代理的基本使用
CGLIB動態(tài)代理也不需要生成代理類,實現(xiàn)MethodInterceptor 就可以了。
注意:CGLib不能代理final的方法
創(chuàng)建抽象主題
注意:CGLb代理的目標對象不需要實現(xiàn)任何接口,就可以通過動態(tài)繼承目標對象實現(xiàn)動態(tài)代理。所以此處可以不用創(chuàng)建接口。直接使用真實主題。
public interface IUser {
public void shopping();
}
創(chuàng)建真實主題
public class User implements IUser{
@Override
public void shopping() {
System.out.println("user shopping....");
}
}
直接使用真實主題。
public class User {
public void shopping() {
System.out.println("user shopping....");
}
}
創(chuàng)建代理主題
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理user,執(zhí)行shopping()開始...");
Object invokeSuper = methodProxy.invokeSuper(o, objects);
System.out.println("代理user,執(zhí)行shopping()結束...");
return invokeSuper;
}
}客戶端調用
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(User.class);
enhancer.setCallback(cglibProxy);
IUser iUser = (IUser) enhancer.create();
iUser.shopping();
}小優(yōu)化
在客戶端調用時,稍顯復雜,可進一步優(yōu)化
public class CglibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
//相當于Proxy,代理工具類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理user,執(zhí)行shopping()開始...");
Object invokeSuper = methodProxy.invokeSuper(o, objects);
System.out.println("代理user,執(zhí)行shopping()結束...");
return invokeSuper;
}
} public static void main(String[] args) throws Exception {
IUser user = (IUser) new CglibProxy().getInstance(User.class);
user.shopping();
}CGLIB與JDK動態(tài)代理區(qū)別
1.執(zhí)行條件
JDK動態(tài)代理實現(xiàn)了被代理對象的接口。CGLb代理的目標對象不需要實現(xiàn)任何接口,它是通過動態(tài)繼承目標對象實現(xiàn)動態(tài)代理。
2.實現(xiàn)機制
JDK動態(tài)代理調用代理方法是由Java內(nèi)部的反射機制來實現(xiàn)的,需要讀取接口信息。CGLib動態(tài)代理是通過FastClass機制來實現(xiàn)的,需要覆蓋父類方法。
3.性能
首先都在運行期生成字節(jié)碼。
JDK動態(tài)代理的代理邏輯簡單,直接寫Class字節(jié)碼,使用反射機制在生成類的過程中比較高效。
CGLib代理實現(xiàn)更復雜,使用ASM框架寫Class字節(jié)碼,但是asm在生成類之后的相關執(zhí)行過程中比較高效。但是可以通過將asm生成的類進行緩存,解決asm生成類過程低效問題。
一句話:CGLib生成代理類比JDK動態(tài)代理效率低,但是執(zhí)行效率比JDK動態(tài)代理高。
到此這篇關于Java結構型設計模式中代理模式示例詳解的文章就介紹到這了,更多相關Java代理模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java計算一個數(shù)加上100是完全平方數(shù),加上168還是完全平方數(shù)
這篇文章主要介紹了Java計算一個數(shù)加上100是完全平方數(shù),加上168還是完全平方數(shù),需要的朋友可以參考下2017-02-02
java使用poi讀取doc和docx文件的實現(xiàn)示例
這篇文章主要介紹了java使用poi讀取doc和docx文件的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03
Java字符串拼接+和StringBuilder的比較與選擇
Java 提供了兩種主要的方式:使用 "+" 運算符和使用 StringBuilder 類,本文主要介紹了Java字符串拼接+和StringBuilder的比較與選擇,感興趣的可以了解一下2023-10-10

