通俗易懂详解Java代理及代码实战
一、概述
代理模式是Java常用的設(shè)計模式之一,實現(xiàn)代理模式要求代理類和委托類(被代理的類)具有相同的方法(提供相同的服務(wù)),代理類對象自身并不實現(xiàn)真正的核心邏輯,而是通過調(diào)用委托類對象的相關(guān)方法來處理核心邏輯,而代理類對象主要負責為委托類對象過濾消息、預(yù)處理消息、轉(zhuǎn)發(fā)消息給委托類、事后處理消息等等。通常代理類會與委托類存在關(guān)聯(lián)關(guān)系。
按照代理的創(chuàng)建時期,代理可分為:靜態(tài)代理和動態(tài)代理。靜態(tài)代理由開發(fā)者手動創(chuàng)建,在程序運行前,已經(jīng)存在;而動態(tài)代理不需要手動創(chuàng)建,它是在程序運行時動態(tài)的創(chuàng)建代理類。二、靜態(tài)代理
我們直接用代碼來說明什么叫靜態(tài)代理,場景是我要賣掉我的車子,但是由于我很忙,所以賣掉車子的過程中不想每天被電話騷擾,于是我就在附近找了一個二手車交易的中介,希望在他的幫助下很輕松的賣掉車子。 1.賣車子接口 public interface SaleCar {void sale(); }2.hafiz真正賣車子實現(xiàn)類
public class HafizSaleCar implements SaleCar {@Overridepublic void sale() {System.out.println("hafiz sale his car...");} }3.二手車交易中介類
public class CarTradeProxy implements SaleCar {private HafizSaleCar owner;public CarTradeProxy(HafizSaleCar owner) {this.owner = owner;}@Overridepublic void sale() {System.out.println("proxy add price...");owner.sale();} }4.測試類
public class Client {public static void main(String[] args) {HafizSaleCar owner = new HafizSaleCar();CarTradeProxy proxy = new CarTradeProxy(owner);proxy.sale();} }5.測試結(jié)果
從上面的代碼中,我們可以看出,其實代理類(CarTradeProxy)和委托類(HafizSaleCar)好像區(qū)別并不大,我們直接創(chuàng)建一個HafizSaleCar對象,然后調(diào)用它的sale()方法不就好了?細心的同學(xué)你會發(fā)現(xiàn),其實代理在真正調(diào)用委托類的方法之前做了中介加價的操作,這也就意味著我們使用代理模式實現(xiàn)在委托類的基礎(chǔ)上增加額外的邏輯操作。
以上就是一個很簡單的靜態(tài)代理的實現(xiàn)過程。但是這個時候我又有了一個新需求,我想用我手里的存款以及買車子賺的錢來給自己買一套新房子,那我又不想東奔西跑找房源,于是我又把買房這件事委托給了房產(chǎn)中介,下面我們就來實現(xiàn)這個邏輯。
1.再定義一個買房的接口
public interface BuyHouse {void buy(); }2.重寫委托類,實現(xiàn)賣車和買房兩個接口
public class HafizTrade implements SaleCar, BuyHouse {@Overridepublic void buy() {System.out.println("hafiz buy house...");}@Overridepublic void sale() {System.out.println("hafiz sale car...");} }可以看到,我現(xiàn)在既要賣掉我的車子,又要購買新的房子。
3.再創(chuàng)建一個買房子的中介代理類
public class HouseTradeProxy implements BuyHouse {private HafizTrade customer;public HouseTradeProxy(HafizTrade customer) {this.customer = customer;}@Overridepublic void buy() {System.out.println("proxy add price...");customer.buy();} }4.賣車子的代理類修改如下
public class CarTradeProxy implements SaleCar {private HafizTrade owner;public CarTradeProxy(HafizTrade owner) {this.owner = owner;}@Overridepublic void sale() {System.out.println("proxy add price...");owner.sale();} }5.新的測試類
public class Client {public static void main(String[] args) {HafizTrade trader = new HafizTrade();CarTradeProxy carTradeProxy = new CarTradeProxy(trader);carTradeProxy.sale();System.out.println("-----------------------------------------------");HouseTradeProxy houseTradeProxy = new HouseTradeProxy(trader);houseTradeProxy.buy();System.out.println("-----------------------------------------------");} }6.測試結(jié)果
這樣通過靜態(tài)代理的方式,我們的確也可以很完美的解決我們的問題,但當我們有越來越多的委托類需要代理,而且代理做的工作又一樣,那是不是會多出來很多的代理類,我們開發(fā)者會瘋掉的,這時候我們就想:如果我們可以只做一次,就能代理一類委托類該多好啊?那么這個時候,動態(tài)代理就應(yīng)運而生了,它可以使得我們只定義一次就能為一類委托類做代理。
三、動態(tài)代理
靜態(tài)代理要求我們在程序發(fā)布上線運行之前,就要開發(fā)好對應(yīng)委托類的代理類,而動態(tài)代理是我們在程序發(fā)布之前,并沒有創(chuàng)建好對應(yīng)的代理類,而是在運行的時候動態(tài)的創(chuàng)建代理類。
動態(tài)代理實現(xiàn)方式有兩種:jdk自帶動態(tài)代理實現(xiàn)以及cglib實現(xiàn)。jdk代理只適合代理實現(xiàn)接口的目標對象,cglib可以代理沒有實現(xiàn)接口的目標對象。
四、基于JDK實現(xiàn)動態(tài)代理
1.實現(xiàn)步驟
1).通過實現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器
2).通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動態(tài)代理類
3).通過反射機制獲得動態(tài)代理類的構(gòu)造函數(shù)(jdk自帶,不需手動處理)
4).通過構(gòu)造函數(shù)創(chuàng)建動態(tài)代理類實例,構(gòu)造時調(diào)用處理器對象作為參數(shù)被傳入(jdk自帶,不需手動處理)
2.創(chuàng)建代理處理器
public class ProxyHandler implements InvocationHandler {private Object target;public ProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("proxy add price...");Object result = method.invoke(target, args);return result;} }3.測試類
public class Client {public static void main(String[] args) {HafizTrade trader = new HafizTrade();ProxyHandler handler = new ProxyHandler(trader);Class<? extends HafizTrade> clazz = trader.getClass();ClassLoader classLoader = clazz.getClassLoader();Class<?>[] interfaces = clazz.getInterfaces();SaleCar carProxy = (SaleCar)Proxy.newProxyInstance(classLoader, interfaces, handler);carProxy.sale();System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");BuyHouse houseProxy = (BuyHouse)Proxy.newProxyInstance(classLoader, interfaces, handler);houseProxy.buy();System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");} }4.測試結(jié)果
5.原理
生成一個代理類,這個代理類繼承Proxy類并且實現(xiàn)了我們定義的接口,代理對象調(diào)用方法的時候,調(diào)用這個代理對象的一個成員InvocationHandler(上面我們傳入了一個InvocationHandler實現(xiàn)對象)的方法,也就是我們包裝了委托類后的方法。
五、基于cglib實現(xiàn)動態(tài)代理
1.實現(xiàn)步驟
1).通過實現(xiàn)CGLib包提供的MethodInterceptor接口,重寫intercept方法,創(chuàng)建自己的方法攔截器
2).通過CGLib中的Enhancer的creat方法創(chuàng)建動態(tài)代理對象
2.添加cglib的maven依賴
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version> </dependency>3.自定義ProxyInterceptor
public class ProxyInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("Trade proxy add price...");Object result = methodProxy.invokeSuper(o, objects);return result;} }4.測試client
public class Client {public static void main(String[] args) {ProxyInterceptor proxy = new ProxyInterceptor();HafizTrade tradeProxy = (HafizTrade)Enhancer.create(HafizTrade.class, proxy);tradeProxy.sale();System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");tradeProxy.buy();} }5.測試結(jié)果
6.原理
首先通過asm字節(jié)碼生成框架生成代理類Class的二進制字節(jié)碼,然后通過Class.forName加載二進制字節(jié)碼,生成Class對象,最后通過反射機制獲取實例構(gòu)造,并初始化代理類對象。
六、總結(jié)
動態(tài)代理可以使得我們一次可以解決一批需要創(chuàng)建代理的問題,使得代碼更加靈活,提高了程序的擴展性。動態(tài)代理在主流java框架中也非常常用,比如最著名的spring,它在AOP的功能就是使用動態(tài)代理實現(xiàn),還有Dubbo等這樣的RPC服務(wù)框架,在客戶端都是通過代理完成服務(wù)的真正調(diào)用。了解和學(xué)會代理以及實現(xiàn)方式能幫助我們更好地理解主流框架。
關(guān)于動態(tài)代理的實現(xiàn)細節(jié),可以參考:http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml#
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的通俗易懂详解Java代理及代码实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows下使用Telnet 命令测
- 下一篇: 谈mvc开发中gzip压缩的应用