java模拟火车站买票的过程_Java常用代理
在我們通常的應用中,代理模式也是我們常用的設計模式之一。所謂的代理模式是指客戶端并不直接調用實際的對象,而是通過調用代理對象,來間接的調用實際的對象。
為什么要采用這種間接的形式來調用對象呢?一般是因為客戶端不想訪問實際的對象,或者訪問實際的對象存在困難,因此通過一個代理對象來完成間接的訪問。
在我們現實生活中,這種情形也是非常常見非常常見的,就比如,黃牛買票,黃牛相當于是火車站的代理,我們可以通過黃牛買票,但只能去火車站進行改簽和退票。
在代碼實現中,相當于為一個委托對象Station(車站)提供一個代理對象Scalper(黃牛),通過Scalper(黃牛)可以調用Station(車站)的部分功能,并添加一些額外的業務處理,同時可以屏蔽Station(車站)中未開放的接口。
代理模式的UML圖
代理模式類圖
1,Station(車站)是委托類,Scalper(黃牛)是代理類;
2,Subject是委托類和代理類的接口;
3,sell()是委托類和代理類的共同方法;
從UML圖中,可以看出代理類與真正實現的類都是實現了抽象的接口,這樣的好處的在于代理類可以與實際的類有相同的方法,可以保證客戶端使用的透明性。
二 Java常用的三種代理
2.1 靜態代理
? ? ? 在代碼實現中相當于為一個委托對象realSubject提供一個代理對象proxy,通過proxy可以調用realSubject的部分功能,并添加一些額外的業務處理,同時可以屏蔽realSubject中未開放的接口。
1、RealSubject 是委托類,Proxy 是代理類;
2、Subject 是委托類和代理類的接口;
3、request() 是委托類和代理類的共同方法;
具體代碼實現如下:
interface Subject { ? ?void request();}class RealSubject implements Subject { ? ?public void request(){
? ? ? ?System.out.println("RealSubject");
? ?}
}class Proxy implements Subject { ? ?private Subject subject; ? ?public Proxy(Subject subject){ ? ? ? ?this.subject = subject;
? ?} ? ?public void request(){
? ? ? ?System.out.println("begin");
? ? ? ?subject.request();
? ? ? ?System.out.println("end");
? ?}
}public class ProxyTest { ? ?public static void main(String args[]) {
? ? ? ?RealSubject subject = new RealSubject();
? ? ? ?Proxy p = new Proxy(subject);
? ? ? ?p.request();
? ?}
}
靜態代理實現中,一個委托類對應一個代理類,代理類在編譯期間就已經確定。
2.2 動態代理
動態代理有以下特點:
在運行期,通過反射機制創建一個實現了一組給定接口的新類;
在運行時生成的class,必須提供一組interface給它,然后該class就宣稱它實現了這些 interface。該class的實例可以當作這些interface中的任何一個來用。但是這個Dynamic Proxy其實就是一個Proxy, 它不會替你作實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。
動態代理也叫做:JDK代理,接口代理
接口中聲明的所有方法都被轉移到調用處理器(handler)一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。而且動態代理的應用使我們的類職責更加單一,復用性更強。
JDK中生成代理對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的寫法是:
注意該方法是在Proxy類中的靜態方法,且接收的三個參數依次為:
ClassLoader loader:指定當前目標對象使用的類加載器,用null表示默認類加載器
Class [] interfaces:需要實現的接口數組
InvocationHandler handler:調用處理器,執行目標對象的方法時,會觸發調用處理器的方法,從而把當前執行目標對象的方法作為參數傳入
java.lang.reflect.InvocationHandler:這是調用處理器接口,它自定義了一個 invoke 方法,用于集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委托類的代理訪問。
? ?// 該方法負責集中處理動態代理類上的所有方法調用。第一個參數是代理類實例,第二個參數是被調用的方法對象,第三個參數是方法參數的數組形式? ?// 第三個方法是調用參數。
? ?Object invoke(Object proxy, Method method, Object[] args)
代碼示例:
package model;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface IUserDao { ? ?void save();}class UserDao implements IUserDao { ? ?public void save() {
? ? ? ?System.out.println("----已經保存數據!----");
? ?}
}class ProxyFactory { ? ?private Object target; ? ?public ProxyFactory(Object target) { ? ? ? ?this.target = target;
? ?} ? ?// 給目標對象生成代理對象,其class文件是由 JVM 在運行時動態生成
? ?public Object getProxyInstance() { ? ? ? ?return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), ? ? ? ? ? ? ? ?new InvocationHandler() { ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ?System.out.println("開始"); ? ? ? ? ? ? ? ? ? ? ? ?// 執行目標對象方法,方法參數是target,表示該方法從屬于target
? ? ? ? ? ? ? ? ? ? ? ?Object returnValue = method.invoke(target, args);
? ? ? ? ? ? ? ? ? ? ? ?System.out.println("提交"); ? ? ? ? ? ? ? ? ? ? ? ?return returnValue;
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?});
? ?}
}public class Client { ? ?public static void main(String[] args) { ? ? ? ?// 目標對象
? ? ? ?IUserDao target = new UserDao();
? ? ? ?System.out.println(target.getClass()); ? ? ? ?// 代理對象
? ? ? ?IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
? ? ? ?System.out.println(proxy.getClass());
? ? ? ?proxy.save();
? ?}
}
輸出:
class model.UserDaoclass model.$Proxy0開始----已經保存數據!----
提交
總結:
代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理
2.3 Cglib代理
上面的靜態代理和動態代理模式都是要求目標對象實現一個接口或者多個接口,但是有時候目標對象只是一個單獨的對象,并沒有實現任何的接口,這個時候就可以使用構建目標對象子類的方式實現代理,這種方法就叫做:Cglib代理。
Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。
Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
Cglib包的底層是通過使用字節碼處理框架ASM來轉換字節碼并生成新的子類
代理的類不能為final,否則報錯;目標對象的方法如果為final/static,那么就不會被攔截。
代碼示例:
目標對象類:UserDao.java
? ? * 目標對象,沒有實現任何接口
? ? */
? ?public class UserDao { ? ? ? ?public void save() {
? ? ? ? ? ?System.out.println("----已經保存數據!----");
? ? ? ?}
? ?}
Cglib代理工廠:ProxyFactory.java
? ?/**? ? * Cglib子類代理工廠
? ?* 對UserDao在內存中動態構建一個子類對象
? ?*/
? ?public class ProxyFactory implements MethodInterceptor{ ? ? ? ? //維護目標對象
? ? ? ?private Object target; ? ? ? ?public ProxyFactory(Object target) { ? ? ? ? ? ?this.target = target;
? ? ? ?} ? ? ? //給目標對象創建一個代理對象
? ? ? ?public Object getProxyInstance(){ ? ? ? ? ? ?//1.工具類
? ? ? ? ? ?Enhancer en = new Enhancer(); ? ? ? ? ? //2.設置父類
? ? ? ? ? ?en.setSuperclass(target.getClass()); ? ? ? ? ? ?//3.設置回調函數
? ? ? ? ? ?en.setCallback(this); ? ? ? ? ? ?//4.創建子類(代理對象)
? ? ? ? ? ?return en.create();
? ? ? ?} ? ? ? ?@Override
? ? ? ?public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
? ? ? ? ? ?System.out.println("開始事務..."); ? ? ? ? ? ?//執行目標對象的方法
? ? ? ? ? ?Object returnValue = method.invoke(target, args);
? ? ? ? ? ?System.out.println("提交事務..."); ? ? ? ? ? ?return returnValue;
? ? ? ?}
? ?}
測試類:
? ?/**? ? * 測試類
? ? */
? ?public class App { ? ? ? ?@Test
? ? ? ?public void test(){ ? ? ? ? ? ?//目標對象
? ? ? ? ? ?UserDao target = new UserDao(); ? ? ? ? ? ?//代理對象
? ? ? ? ? ?UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); ? ? ? ? ? ?//執行代理對象的方法
? ? ? ? ? ?proxy.save();
? ? ? ?}
? ?}
總結
以上是生活随笔為你收集整理的java模拟火车站买票的过程_Java常用代理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公积金提前还款省钱吗
- 下一篇: 信用卡封了多久能恢复