簡單學習java設計模式的 靜態代理模式和動態代理模式

代理模式概念:

理模式的定義
1、爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用,比如我們要去法院申訴,中間需要一個律師幫忙代理一下注意事項等,這個律師就是代理類。
2、當一個複雜對象的多份副本須存在時,代理模式可以結合享元模式以減少存儲器用量。典型作法是創建一個複雜對象及多個代理者,每個代理者會引用到原本的複雜對象。而作用在代理者的運算會轉送到原本對象。一旦所有的代理者都不存在時,複雜對象會被移除。
在這裏插入圖片描述
抽象角色:通過接口或抽象類聲明真實角色實現的業務方法。
代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,可以附加自己的操作。
真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。

一個是真正的你要訪問的對象(目標類),一個是代理對象,真正對象與代理對象實現同一個接口,先訪問代理類再訪問真正要訪問的對象。

代理模式分爲靜態代理、動態代理。

靜態代理是由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就確定了。

動態代理是在實現階段不用關心代理類,而在運行階段才指定哪一個對象。

只看概念有點懵,現在舉個例子,比如我們人要去法院申訴用代碼演示

靜態代理:

1、我們先創建一個接口類打官司接口Law

//打官司接口
public interface Law {
   public void law();
}

2、現在是我們進行申訴 目標類

public class Person implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println("目標類去法庭申述");
 }

}

3、現在是靜態代理的核心部分,律師代理類

//代理類
public class Lawyer implements Law {
   
 //引入目標類
 private Person person; 
 @Override 
 //核心
 public void law() {
  // TODO Auto-generated method stub
      this.a();
      //person對象實例化,只有實例化之後,才能將這個對象放到內存中,然後才能在規定的範圍內來調用
      if(person==null) {
       person=new Person();
      }
    //調用目標類的方法
      person.law();
      
      this.b();
 }
 
 //律師要處理的事情
 public void a() {
  System.out.println("開庭前收集證據");
 }
 
 public void b() {
  System.out.println("案件結案處理");
 }

}

靜態代理類比較好理解,現在直接創建客戶端測試一下
4、創建客戶

public class test {

 public static void main(String[] args) {
  // TODO Auto-generated method stub

  Lawyer lawyer=new Lawyer();
  lawyer.law();
 }

}

運行結果:
在這裏插入圖片描述

上面就是靜態代理模式,比較簡單,我們就不解釋了,動態代理不同於靜態代理的特點是它更爲靈活,因爲動態代理就是在運行期間動態生成代理類。我們沿用上面的例子,假設有五百個不一樣的人要打官司,都交給律師來操辦,那麼按照靜態代理的思路來做,我們需要寫五百個真實角色,並且代理角色持有這五百個真實角色。這顯然不合邏輯。這時候動態代理就應運而生了。下面我們要演示動態代理模式。

動態代理類的接口類和目標類跟靜態代理類一樣
1、打官司接口Law

//打官司接口
public interface Law {
   public void law();
}

2、目標類

public class Person implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println(this.getClass().getSimpleName()+"目標類去法庭申述");
 }

}

3、現在我們需要創建一個對象類去實現動態代理的一個核心接口InvocationHandler,動態代理本質採用的Java反射機制實現
InvocationHandler 是一個接口,官方文檔解釋說,每個代理的實例都有一個與之關聯InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//實現動態代理的一個核心接口,動態代理本質採用的Java反射機制實現
//InvocationHandler 是一個接口,官方文檔解釋說,每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,
//如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。
public class MyHandler implements InvocationHandler {
 
 private Object object;
 
 public MyHandler(Object object) {
 
  this.object=object;
 }


// InvocationHandler 內的 invoke() 方法決定了怎麼樣處理代理傳遞過來的方法調用。
// proxy 代理對象
// method 代理對象調用的方法
// args  調用的方法中的參數
// Proxy 動態產生的代理會調用 InvocationHandler 實現類,因此 InvocationHandler 是實際執行者。
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // TODO Auto-generated method stub
  System.out.println("開庭前律師收集證據");
  Object obj=method.invoke(object, args);
  System.out.println("目標類陳述事實");
  return obj;

}

上面用到 InvocationHandler 內的 invoke() 方法決定了怎麼樣處理代理傳遞過來的方法調用,裏面的三個參數分別代表
// proxy 代理對象
// method 代理對象調用的方法
// args 調用的方法中的參數
// Proxy 動態產生的代理會調用 InvocationHandler 實現類,因此 InvocationHandler 是實際執行者。

4、我們直接去創建一個客戶端去實現動態代理:

import java.lang.reflect.Proxy;
public class test {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
      //代理目標類
  Law law=new Person();
   //生成代理對象,Proxy類通過調用newProxyInstance這個方法生產代理對象
  //動態代碼涉及了一個非常重要的類 Proxy,通過 Proxy 的靜態方法 newProxyInstance 纔會動態創建代理。
  //它的 3 個參數意義。
        //loader即這裏的Law.class.getClassLoader() 自然是類加載器
        //interfaces即new Class[]{Law.class} 代碼要用來代理的接口
        //h即new MyHandler(law) 一個 InvocationHandler 對象     
     Law proxy=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[]  {Law.class}, new MyHandler(law));
     proxy.law();
 }


// 上面執行person類的結果,那麼如果律師接待第二個人Other呢?我們直接載加一個Other對象,然後客戶端直接加下面代碼就可以了
     Law law1=new Other();
     Law proxy1=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[]{Law.class}, new MyHandler(law1));
     proxy1.law();


}

生成代理對象Proxy類通過調用newProxyInstance這個方法生產代理對象
動態代碼涉及了一個非常重要的類 Proxy,通過 Proxy 的靜態方法 newProxyInstance 纔會動態創建代理。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

//它的 3 個參數意義。
//loader即這裏的Law.class.getClassLoader() 自然是類加載器
//interfaces即new Class[]{Law.class} 代碼要用來代理的接口
//h即new MyHandler(law) 一個 InvocationHandler 對象

添加的第二個人Other:

public class Other implements Law {

 @Override
 public void law() {
  // TODO Auto-generated method stub
       System.out.println(this.getClass().getSimpleName()+"目標類去法庭申述");
 }

}

運行結果:
在這裏插入圖片描述

上面就演示完靜態代理模式和動態代理模式了,如果還是很懵,可以去https://www.jianshu.com/p/d9e1641fbb6c查看,那裏有詳細介紹Proxy.newInstance(…)、getProxyClass0(loader, intfs)源碼

動態代理模式和靜態代理模式的應用場景:

靜態代理主要用來處理少部分類的託管或者擴展。靜態代理對於被代理的對象很固定,我們只需要去代理一個類或者若干固定的類,數量不是太多的時候,可以使用,而且其實效果比動態代理更好。

動態代理在運行期間動態生成代理類,需要消耗的時間會更久一點。優點是可以做很多類的擴展,而且如果一個類的接口發生了變化,那麼靜態代理這時候修改起來就很麻煩了。這就是說動態代理靈活的原因。
動態代理主流的實現有兩種,一種是基於接口的Java Proxy的代理機制,一種是基於繼承的cglib代理機制。兩種也都有其應用場景。

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