設計模式之代理模式

在java的項目中經常用到代理模式,它不是什麼高深的技術,只是一種解決問題的思路。

代理模式的由來

       程序來源於生活,前賢的總結總是那麼精闢。在生活中處處可以見到代理模式的影子。如明星和經紀人與業務方(廣告商,導演),租房客和中介與房東,大姑娘和媒婆與小夥子。發現它們的規律了嗎?參與方總是有三方,通過中間商賺差價...呸,通過中間商提供溝通的橋樑,不用親力親爲,還能享受額外的服務,例如經紀人除了告訴明星關於導演廣告商提供的信息,還能提前篩選收集資料,分析可行性。中介除了可以給租客介紹房子帶看房,還能給介紹房子的相關朝向,過戶流程之類的額外服務。

其實總結下來就是兩點

  1.不用爲一些事情投入過多精力,親力親爲

  2.通過中間人,能獲得額外的服務和信息

轉到程序裏面就是

 1.有業務需要調用外部接口,內部或者外部可能經常變更的時候,可以採用代理模式,內部業務不需要動,只改代理類就行了。這樣代碼無侵入性,無耦合

 2.代理能在接口調用前後做額外工作,如記錄調用時間,次數,能數據校驗,攔截等

代理模式的實現方式

 1.靜態代理

 2.動態代理

    a. cglib方式

    b.jdk方式

靜態代理是比較容易理解的,其結構不外乎,創建一個類,裏面持有被用者的引用對象,並提供一個方法,供使用者調用。需要調用的時候,找代理類就行,你不用關心他怎麼實現的

動態代理其實就是靜態代理類,只不過是由jvm通過反射(asm)動態生成一個匿名代理類.class文件.接下來可以瞭解一下它們的使用與要點。

前置需求

有一個下班接口,有方法go,需要在下班前下班後做點事情

interface OffWork{
void go();
}

 有一個實現類

public class Employee implements OffWork {
    public  Employee(){}
    private String name;

    public Employee(String name) {
        this.name = name;
    }

    @Override
    public void go() {
        System.out.println("員工:"+name+"下班了");
    }
}

 


 

靜態代理

優點:結構簡單,實現也簡單,一目瞭然

缺點:項目中有大量代理類,結構一致,代碼重複,導致項目包文件過大。新增接口麻煩,接口變動代理類也得跟着變動,就算有工具可以生成代理類,也得替換和檢查

 1 public class StaticProxy {
 2     public StaticProxy(OffWork e){
 3         this.work=e;
 4     }
 5    private OffWork work;
 6     public void go(){
 7         System.out.println("下班前準備,好像需要關電腦...");
 8         work.go();
 9         System.out.println("下班路上順便買個菜");
10     }
11 }

 


動態代理-cglib方式

步驟:

 1.創建代理類並實現MethodInterceptor 接口

2.持有調用者引用,並提供一個create方法,返回匿名代理類

3.在調用方法前後做額外工作before,after

 1 public class CglibProxy implements MethodInterceptor {
 2     private Object target;
 3 
 4     public Object create(Object target) {
 5         this.target = target;
 6         Enhancer enhancer = new Enhancer();
 7         enhancer.setSuperclass(target.getClass());
 8         enhancer.setCallback(this);
 9         return enhancer.create();
10     }
11 
12     @Override
13     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
14         before();
15         Object res=   method.invoke(target, objects);
16         after();
17         return res;
18     }
19     private void before(){
20         System.out.println("執行之前找點事情乾乾");
21     }
22     private void after(){
23         System.out.println("完成之後收工");
24     }
25 }

 

 注意!劃重點

以上cglib方式只能用於無參構造,。

下面是有參構造,注意他們之前的區別

 1 public class CglibProxy implements MethodInterceptor {
 2     private Object target;
 3 
 4     public Object create(Object target) {
 5         this.target = target;
 6         Enhancer enhancer = new Enhancer();
 7         enhancer.setSuperclass(target.getClass());
 8         enhancer.setCallback(this);
 9         return enhancer.create();
10     }
11     public <T> T getInstance(T target,Class[] args,Object[] argsValue){
12         Enhancer enhancer = new Enhancer();
13         enhancer.setSuperclass(target.getClass());
14         enhancer.setCallback(this);
15         return (T) enhancer.create(args,argsValue);
16     }
17     @Override
18     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
19         before();
20         Object res=    methodProxy.invokeSuper(o, objects);
21         /**被註釋的是無參構造用法**/
22        //   Object res=method.invoke(target, objects);
23         after();
24         return res;
25     }
26     private void before(){
27         System.out.println("執行之前找點事情乾乾");
28     }
29     private void after(){
30         System.out.println("完成之後收工");
31     }
32 }

 

 

動態代理-jdk方式

在java.lang.reflect包下面InvocationHandler接口 

步驟:

1.創建代理類並實現InvocationHandler接口

2.創建成員變量,用來存儲調用者的引用

3.提供create接口,通過方法注入,給成員變量賦值,返回匿名代理類

4.在invoke方法中調用接口,並添加額外業務

 1 public class DynamicProxy implements InvocationHandler {
 2     private Object target;
 3 
 4 
 5     @Override
 6     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 7         before();
 8         Object res=  method.invoke(target,args);
 9         after();
10         return res;
11     }
12     public Object create(Object t ){
13         target=t;
14         return    Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
15     }
16     private void before(){
17         System.out.println("執行之前找點事情乾乾");
18     }
19     private void after(){
20         System.out.println("完成之後收工");
21     }
22 }

 

調用

 1 public class MainTest {
 2     public static void main(String[] args) {
 3         /**靜態代理方式**/
 4         OffWork employee=  new Employee("張一山");
 5         new StaticProxy(employee).go();
 6         System.out.println("==========================");
 7         /**jdk動態代理方式**/
 8         DynamicProxy   dynamicProxy=   new DynamicProxy();
 9         OffWork employee1=(OffWork) dynamicProxy.create(employee);
10         employee1.go();
11         System.out.println("==========================");
12         /**cglib動態代理方式**/
13         CglibProxy cglibProxy=new CglibProxy();
14         /**無參構造方式**/
15         OffWork offWork= (OffWork)cglibProxy.create(employee);
16         offWork.go();
17         System.out.println("==========================");
18         /**有參構造方式**/
19         OffWork s=  cglibProxy.getInstance(employee,new Class[]{String.class},new Object[]{"張一山"});
20        s.go();
21     }
22 }

 

 

 

 

 輸出結果

下班前準備,好像需要關電腦...
員工:張一山下班了
下班路上順便買個菜
==========================
執行之前找點事情乾乾
員工:張一山下班了
完成之後收工
==========================
執行之前找點事情乾乾
員工:null下班了
完成之後收工
==========================
執行之前找點事情乾乾
員工:張一山下班了
完成之後收工

 

 分析

我們常用的動態代理jdk方式和cglib 有什麼不同呢,如何選擇?

這就要聊到它們的實現方式上了。jdk動態代理和cglib動態代理。兩種方法的存在,各有各自的優勢。

jdk方式 InvocationHandler

jdk動態代理是由java內部的反射機制來實現的

特點:

    jdk反射機制在生成類的過程中比較高效,在執行時效率較低

優點:無依賴,直接利用jdk反射

缺點:被代理的類必須實現接口方法,否則無法編譯通過,無法使用

 

cglib方式 MethodInterceptor

cglib動態代理底層則是藉助asm來實現。

特點:生成代理類的過程中比較慢,但生成後使用速度快

優點:

    被代理類無需實現接口,普通類就可以了,沒有侷限性

 缺點:有外部依賴,不過目前已經被封裝到spring-core.jar中

 

相比較來說,cglib的使用範圍更廣泛,更通用一點

 總結

    動態代理給我們程序添加了無數的可能,或者可以這樣說,動態編譯+反射+動態代理 給java程序帶來了很多奇思妙想,spring的核心也是在這裏,利用它實現了容器的加載,方法的增強,ioc 與aop 都是通過它們實現的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章