通州 網站建設自己怎樣在百度上做推廣
在我們接下來聊Spring AOP之前我們先了解一下設計模式中的代理模式。
一、代理模式
代理模式是23種設計模式中的一種,它屬于結構型設計模式。
對于代理模式的理解:
- 程序中對象A與對象B無法直接交互,如:有人要找某個公司的老總得先打前臺登記傳達
- 程序中某個功能需要在原基礎上增強,如:攝像頭本來是來錄像的,但是現在有一種攝像頭除了有基本的錄像功能,還可以自動檢測到異常后聯(lián)網報警
- 程序中某個對象需要被保護,它不支持面對客戶,這個時候可以使用一個代理類對象來與客戶進行交互,對于客戶來說使用這個代理類對象與使用被保護的目標對象沒有區(qū)別
- 程序在在調用目標對象的前后需要做一些額外的工作,如:記錄下誰調用了這個目標方法,調用的效率如何,是否要校驗權限來保證安全
代理模式的作用:為其他對象提供一種代理以控制對這個對象的訪問。在有些情況下,一個客戶不想或者不能直接引用一個對象,這個時候就可以通過一個稱為"代理"的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到中介的作用,并且可以通過代理對象去掉客戶端不應該看到的內容和服務或者是添加客戶需要的額外服務。通過引用一個新的對象來實現對真實對象的操作或者把新的對象作為真實對象的一個”替身“。這種實現機制就是代理模式。
代理模式中的角色有哪些:
- 代理類(代理角色)
- 目標類(真實目標角色)
- 代理類與目標類的公共接口(抽象角色)
代理類在代碼實現上分為兩種形式:
動態(tài)代理
靜態(tài)代理
靜態(tài)代理
示例有如下的接口與實現類
public interface OrderService {/*** 生成訂單*/void generate();/*** 查看訂單詳情*/void detail();/*** 修改訂單*/void modify();
}
public class OrderServiceImpl implements OrderService {@Overridepublic void generate() {try {Thread.sleep(1520);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單生成成功");}@Overridepublic void detail() {try {Thread.sleep(2300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單信息如下:*******");}@Overridepublic void modify() {try {Thread.sleep(1020);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單修改成功");}
}
上面是正常的業(yè)務代碼,當項目上線一段時間后,發(fā)現系統(tǒng)運行有些慢,這個時候我們希望知道是哪里慢了,這個需要如何處理呢?
處理方案一:最直接的方式是,在實現類中每個方法運行代碼中添加統(tǒng)計耗時的邏輯
public class OrderServiceImpl implements OrderService {@Overridepublic void generate() {long begin = System.currentTimeMillis();try {Thread.sleep(1520);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單生成成功");System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();try {Thread.sleep(2300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單信息如下:*******");System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();try {Thread.sleep(1020);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("訂單修改成功");System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}
}
這種方式的優(yōu)點是邏輯清晰,是可行的方案,但是對于功能的修改要改源碼,這違背了OCP開閉原則。
第二種方案:編寫一個子類,這個子類繼承現在的實現類,在這個子類中重寫每個方法,添加統(tǒng)計耗時的邏輯
public class OrderServiceImplSub extends OrderServiceImpl{@Overridepublic void generate() {long begin = System.currentTimeMillis();super.generate();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();super.detail();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();super.modify();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}
}
這種方案也是可行的,對擴展開放,對修改關閉,符合OCP開閉原則,但是有如下問題:
- 如果系統(tǒng)中的類比較多,每個類哪不都要寫一個子類?
- 由于采用了繼承的方式,導致代碼之間的耦合度增高了
第三種處理方案:使用代理模式(靜態(tài)代理)
public class OrderServiceProxy implements OrderService { // 代理類與目標類實現同一接口// 目標對象private OrderService target;public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic void generate() {long begin = System.currentTimeMillis();target.generate();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();target.detail();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();target.modify();System.out.println("耗時:" + (System.currentTimeMillis() - begin) + "ms");}
}
public class Client {public static void main(String[] args) {// 創(chuàng)建目標對象OrderServiceImpl target = new OrderServiceImpl();// 創(chuàng)建代理對象OrderServiceProxy proxy = new OrderServiceProxy(target);// 調用代理對象的方法proxy.generate();proxy.modify();proxy.detail();}
}
上面的三種方案中第三種方案相對可取,它就是靜態(tài)代理的方式。其中OrderService接口是代理類和目標類的共同接口, OrderServiceImpl是目標類,OrderServiceProxy是代理類。
現在有一個問題:如果業(yè)務接口很多,一個接口對應一個代理類,顯然也是不合理的,這有可能導致類爆炸。如何解決這個問題呢?這個時候可以考慮動態(tài)代理。因為動態(tài)代理當中可以在內存中動態(tài)生為我們生成代理類的字節(jié)碼。代理類不需要我們寫了。類爆炸的問題就自然解決了。需要復用的代碼也只需要寫一次了,代碼也能得到復用了。
動態(tài)代理
程序運行階段,在內存中動態(tài)生成代理類,稱之為動態(tài)代理。目的是為了減少代理類的數量。解決代碼復用的問題。
在內存中動態(tài)生成類的字節(jié)碼常見方式有如下三個:
- JDK動態(tài)代理技術:只能代理接口
- CG