上一篇文章《JAVA設計模式之模板方法模式和建造者模式》談到了設計模式中建造類的模式,咱們來回顧下。模板方法模式定義了核心的算法結構,而後子類能夠實現某些特定結構的算法內容,建造者模式徹底把核心的算法內容交給子類去實現。咱們這篇博文來學習下最經常使用的設計模式,代理模式。java
定義:Provide a surrogate or placeholder for another object to control access to it.算法
翻譯:爲其餘對象提供一種代理以控制對這個對象的訪問。設計模式
代理模式仍是比較好理解,就是委託別人作事情,好比咱們要租房子,委託中介去找房子,這就是代理模式。代理模式分爲靜態代理模式和動態代理模式,咱們來結合代碼看看代理模式如何實現的。bash
2.1 靜態代理框架
/***
定義消費者接口,查找指定區域,指訂價格如下的房子*/
public interface ICustomer {
/**查找房子,指定區域,指訂價格*/
String findHouse(String location,int price);
}
/**真正的找房消費者,因此只關注找到這個目標**/
public class YungCustomer implements ICustomer {
@Override
public String findHouse(String location,int price) {
System.out.println("找到了位於["+location+"],價格"+price+"如下的房子");
return "找到了位於["+location+"],價格"+price+"如下的房子";
}
}
/**找房子中介類,找到合適房子再反饋給指消費者**/
public class AgencyProxy implements ICustomer{
private ICustomer coustomer;
public AgencyProxy(ICustomer coustomer) {
this.coustomer = coustomer;
}
@Override
public String findHouse(String location,int price) {
String result="沒有符合條件的房子";
before();
if (null != coustomer){
result= coustomer.findHouse(location,price);
}
after();
return result;
}
private void before(){
System.out.println("開始找房子");
}
private void after(){
System.out.println("結束找房子");
}
}
/***場景類*/
public class Client {
public static void main(String[] args) {
ICustomer customer = new YungCustomer();
ICustomer proxy = new AgencyProxy(customer);
String result = proxy.findHouse("釣魚島附近", 2000);
System.out.println(result);
}
}
複製代碼
輸出結果:maven
開始找房子
找到了位於[釣魚島附近],價格2000如下的房子
結束找房子
複製代碼
靜態代理模式比較簡單,總結就是代理類中包含實際調用者的引用,當符合條件時候,在去調用真正的對象方法。分佈式
2.2 JDK動態代理ide
動態代理比較複雜一點,相對於靜態代理的指定代理對象,動態代理是在運行時候才知道實際代理對象。動態代理應用比較普遍,咱們用的最多的框架Spring中 AOP就採用了動態代理。咱們來把上面的代碼改形成動態代理。post
/**動態代理handler**/
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result= method.invoke(object,args);
after();
return result;
}
private void before(){
System.out.println("開始找房子");
}
private void after(){
System.out.println("結束找房子");
}
}
public class Client {
public static void main(String[] args) {
// 獲取動態代理對象。
ICustomer customer = (ICustomer) Proxy.newProxyInstance(Client.class.getClassLoader()
, new Class[]{ICustomer.class}
, new MyInvocationHandler(new YungCustomer()));
customer.findHouse("釣魚島附近", 2000);
}
}
複製代碼
輸出結果:性能
開始找房子
找到了位於[釣魚島附近],價格2000如下的房子
結束找房子
複製代碼
動態代理要求代理的類必需要有接口,同時實現InvocationHandler的接口,實現接口的invoke方法便可,在invoke方法內能夠在真正執行方法的先後添加想作的事情,好比說記錄日誌,通知消息等等,這就是AOP的思想,在不改變原有業務代碼的狀況下,添加功能。可是jdk動態代理的缺點就是代理的類必需要有接口才行,爲了彌補這個缺點,出現了一款不須要接口就能被代理的第三方庫,CGLIB庫。
2.3.CGLIB動態代理
2.3.1 CGLIB介紹
CGLIB是一個強大的、高性能的代碼生成庫。它被普遍使用在基於代理的AOP框架(例如Spring AOP和dynaop)提供方法攔截。在實現內部,CGLIB庫使用了ASM這一個輕量但高性能的字節碼操做框架來轉化字節碼,產生新類。
2.3.2 CGLIB的使用
既然是第三方庫,咱們須要添加maven依賴
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
複製代碼
CGLIB也和jdk動態代理同樣,須要設置回調方法,在JDK動態代理中咱們要實現 InvocationHandler,而在CGLIB中咱們須要實現net.sf.cglib.proxy.MethodInterceptor
接口做爲回調接口。咱們先看代碼
/**實現回調接口**/
public class MyMthondInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o,objects);
after();
return result;
}
private void before(){
System.out.println("開始找房子");
}
private void after(){
System.out.println("結束找房子");
}
}
public class Client {
public static void main(String[] args) {
//建立回調實例
MyMthondInterceptor interceptor=new MyMthondInterceptor();
//CGLIB建立實例
Enhancer enhancer = new Enhancer();
//設置須要代理的類
enhancer.setSuperclass(YungCustomer.class);
//設置回調類
enhancer.setCallback(interceptor);
//獲取代理對象
YungCustomer customer= (YungCustomer) enhancer.create();
//執行方法
customer.findHouse("釣魚島",1000);
}
}
複製代碼
輸出結果:
開始找房子
找到了位於[釣魚島],價格1000如下的房子
結束找房子
複製代碼
這裏CGLIB實現結果和JDK動態代理徹底同樣。上面實現回調方法的時候須要注意,推薦使用methodProxy.invokeSuper(o,objects);
,這裏調用的是CJLIB庫的方法,若是使用method.invoke(o,args);
須要注意的是,這裏使用的就是JDK的動態代理了,同時invoke的object必須是傳入的代理實例,而不是方法中形參object,不然會致使死循環調用。同時考慮到性能,仍是建議使用第一種調用方式。
CGLIB庫還提供了不少其餘的特性,好比回調方法過濾等等。有興趣的同窗能夠自行研究。
優勢
1.職責清晰
真實的角色實現實際的業務邏輯,不用關心其餘非核心的業務邏輯。業務是業務,輔助功能是輔助功能,職責很是清晰。好比要實現日誌功能,不用耦合在實際的業務代碼中,只要作一個代理便可。
2.良好的擴展性
因爲核心的業務邏輯已經封裝好了,後面要加強業務功能,也可使用代理模式代理增長功能便可。
缺點
1.類的臃腫
對於靜態代理來講,若是過多的使用靜態代理會帶來類臃腫。通常會在接口協議轉換中使用比較多代理模式。 2.複雜性 對於動態代理來講,設計到回調方法的實現,特別是CGLIB中的使用,仍是帶來了必定的複雜性。
3.性能
對於動態代理來講,都是運行期進行字節碼操做,因此仍是帶來了一些性能損耗,可是這不能做爲不使用動態代理的理由,任何東西都是有兩面性的。
1.方法加強;好比增長日誌,事務等功能。
2.遠程RPC調用;如今不少分佈式系統RPC調用都是採用了代理模式。
3.協議等的轉換;好比須要適用另一套協議接口,會使用代理模式,先轉換爲老協議,而後再調用實際的類去執行。
4.懶加載;有些框架會在開始的時候使用代理類來替換實際類,等到真正要使用該類的時候才進行加載,從而達到了懶加載的效果。
本篇博客介紹了代理模式,代理模式分爲靜態代理和動態代理,動態代理又分爲jdk動態代理和CGLIB動態代理。JDK動態代理必需要有接口才能使用,CGLIB彌補了這個缺陷,能夠直接對類進行代理。同時CGLIB動態代理性能相對於JDK動態代理要優秀。
《設計模式之禪》