Java代理模式

小張是一個普普統統的碼農,天天勤勤懇懇地碼代碼。某天中午小張剛要去吃飯,一個電話打到了他的手機上。「是XX公司的小張嗎?我是YY公司的王AA」。「哦,是王總啊,有什麼事情嗎?」。溝經過後,小張弄明白了,原來客戶有個需求,恰好負責這方面開發的是小張,客戶就直接找到了他。不太小張卻沒有答應客戶的請求,而是讓客戶找產品經理小李溝通。html

是小張着急去吃麪而甩鍋嗎?並非,只是爲了使故事能夠套到代理模式上。咱們先看一下代理模式的定義: * 爲其餘對象提供一種代理,以控制對這個對象的訪問。(Provide a surrogate or placeholder for another object to control access to it)java

對照定義,碼農小張能夠映射爲其餘對象,產品經理小李爲小張的代理。咱們經過JAVA代碼,表述上面事例。程序員

靜態代理

1.抽象角色

基於面向對象的思想,首先定義一個碼農接口,它有一個實現用戶需求的方法。app

1jvm

2ide

3函數

4工具

5源碼分析

//定義一個碼農接口,他能夠實現用戶需求
public interface ICoder {
    public void imlDemands(String demandName);
}this

2.真實角色

咱們假設小牛是JAVA程序員,定義一個JAVA碼農類,他經過JAA語言實現需求。

1

2

3

4

5

6

7

8

9

10

11

12

13

public class JavaCoder implements ICoder{
    private String name;   
    public JavaCoder(String name) {
        super();
        this.name = name;
    }

    @Override
    public void imlDemands(String demandName) {
        // TODO Auto-generated method stub
        System.out.println(name+"實現了方法:"+demandName+"in java");
    }   
}

3.代理角色

委屈一下產品經理,將其命名爲碼農代理類,同時讓他實現ICoder接口。

1

2

3

4

5

6

7

8

9

10

11

12

13

public class CoderProxy implements ICoder{

    private ICoder coder;

    public CoderProxy(ICoder coder){

        this.coder = coder;

    }

    @Override

    public void implDemands(String demandName) {

        coder.implDemands(demandName);

    }

}

上面一個接口,兩個類,就實現了代理模式。Are you kidding me?這麼簡單?是的,就是這麼簡單。 咱們經過一個場景類,模擬用戶找產品經理增長需求。

1

2

3

4

5

6

7

8

9

10

11

public class Customer {

    public static void main(String args[]){

        //定義一個java碼農

        ICoder coder = new JavaCoder("Niu");

        //定義一個產品經理

        ICoder proxy = new CoderProxy(coder);

        //讓產品經理實現一個需求

        proxy.implDemands("Add user manageMent");

    }

}

運行程序,結果以下:

1

Niu implemented demand:Add user manageMent in JAVA!

產品經理充當了程序員的代理,客戶把需求告訴產品經理,並不須要和程序員接觸。看到這裏,有些機智的程序員發現了問題。你看,產品經理就把客戶的需求轉達了一下,怪不得我看產品經理這麼不爽。

產品經理固然不僅是轉達用戶需求,他還有不少事情能夠作。好比,該項目決定不接受新增功能的需求了,對修CoderProxy類作一些修改:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class CoderProxy implements ICoder{

    private ICoder coder;

    public CoderProxy(ICoder coder){

        this.coder = coder;

    }

    @Override

    public void implDemands(String demandName) {

        if(demandName.startsWith("Add")){

            System.out.println("No longer receive 'Add' demand");

            return;

        }

        coder.implDemands(demandName);

    }

}

這樣,當客戶再有增長功能的需求時,產品經理就直接回絕了,程序員無需再對這部分需求作過濾。

總結

咱們對上面的事例作一個簡單的抽象:

代理模式包含以下角色:

  • Subject:抽象主題角色。能夠是接口,也能夠是抽象類。
  • RealSubject:真實主題角色。業務邏輯的具體執行者。
  • ProxySubject:代理主題角色。內部含有RealSubject的引用,負責對真實角色的調用,並在真實主題角色處理先後作預處理和藹後工做。

代理模式優勢:

  • 職責清晰 真實角色只需關注業務邏輯的實現,非業務邏輯部分,後期經過代理類完成便可。
  • 高擴展性 無論真實角色如何變化,因爲接口是固定的,代理類無需作任何改動。

動態代理

前面講的主要是靜態代理。那麼什麼是動態代理呢?

假設有這麼一個需求,在方法執行前和執行完成後,打印系統時間。這很簡單嘛,非業務邏輯,只要在代理類調用真實角色的方法前、後輸出時間就能夠了。像上例,只有一個implDemands方法,這樣實現沒有問題。但若是真實角色有10個方法,那麼咱們要寫10遍徹底相同的代碼。有點追求的碼農,確定會對這種方法感到很是不爽。有些機智的小夥伴可能想到了用AOP解決這個問題。很是正確。莫非AOP和動態代理有什麼關係?沒錯!AOP用的偏偏是動態代理。

代理類在程序運行時建立的代理方式被稱爲動態代理。也就是說,代理類並不須要在Java代碼中定義,而是在運行時動態生成的。相比於靜態代理, 動態代理的優點在於能夠很方便的對代理類的函數進行統一的處理,而不用修改每一個代理類的函數。對於上例打印時間的需求,經過使用動態代理,咱們能夠作一個「統一指示」,對全部代理類的方法進行統一處理,而不用逐一修改每一個方法。下面咱們來具體介紹下如何使用動態代理方式實現咱們的需求。

與靜態代理相比,抽象角色、真實角色都沒有變化。變化的只有代理類。所以,抽象角色、真實角色,參考ICoder和JavaCodr。

在使用動態代理時,咱們須要定義一個位於代理類與委託類之間的中介類,也叫動態代理類,這個類被要求實現InvocationHandler接口:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class CoderDynamicProxy implements InvocationHandler{

     //被代理的實例

    private ICoder coder;

    public CoderDynamicProxy(ICoder _coder){

        this.coder = _coder;

    }

    //調用被代理的方法

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(System.currentTimeMillis());

        Object result = method.invoke(coder, args);

        System.out.println(System.currentTimeMillis());

        return result;

    }

}

當咱們調用代理類對象的方法時,這個「調用」會轉送到中介類的invoke方法中,參數method標識了咱們具體調用的是代理類的哪一個方法,args爲這個方法的參數。

咱們經過一個場景類,模擬用戶找產品經理更改需求。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class DynamicClient {

     public static void main(String args[]){

            //要代理的真實對象

            ICoder coder = new JavaCoder("Zhang");

            //建立中介類實例

            InvocationHandler  handler = new CoderDynamicProxy(coder);

            //獲取類加載器

            ClassLoader cl = coder.getClass().getClassLoader();

            //動態產生一個代理類

  ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);

            //經過代理類,執行doSomething方法;

            proxy.implDemands("Modify user management");

        }

}

執行結果以下:

1

2

3

1521810491379

Niu implemented demand:Modify user management in JAVA!

1521810491380

經過上述代碼,就實現了,在執行委託類的全部方法前、後打印時間。仍是那個熟悉的小牛,但咱們並無建立代理類,也沒有時間ICoder接口。這就是動態代理。

總結

總結一下,一個典型的動態代理可分爲如下四個步驟:

  1. 建立抽象角色
  2. 建立真實角色
  3. 經過實現InvocationHandler接口建立中介類
  4. 經過場景類,動態生成代理類

若是隻是想用動態代理,看到這裏就夠了。但若是想知道爲何經過proxy對象,就可以執行中介類的invoke方法,以及生成的proxy對象是什麼樣的,能夠繼續往下看。

源碼分析(JDK7)

看到這裏的小夥伴,都是有追求的程序員。上面的場景類中,經過

1

2

//動態產生一個代理類

ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);

動態產生了一個代理類。那麼這個代理類是如何產生的呢?咱們經過代碼一窺究竟。

Proxy類的newProxyInstance方法,主要業務邏輯以下:

1

2

3

4

5

6

//生成代理類class,並加載到jvm中

Class<?> cl = getProxyClass0(loader, interfaces);

//獲取代理類參數爲InvocationHandler的構造函數

final Constructor<?> cons = cl.getConstructor(constructorParams);

//生成代理類,並返回

return newInstance(cons, ih);

上面代碼作了三件事:

  • 根據傳入的參數interfaces動態生成一個類,它實現interfaces中的接口,該例中即ICoder接口的implDemands方法。假設動態生成的類爲$Proxy0。
  • 經過傳入的classloder,將剛生成的$Proxy0類加載到jvm中。
  • 利用中介類,調用$Proxy0的$Proxy0(InvocationHandler)構造函數,建立$Proxy0類的實例,其InvocationHandler屬性,爲咱們建立的中介類。

上面的核心,就在於getProxyClass0方法:

1

2

3

4

5

6

7

8

9

10

11

private static Class<?> getProxyClass0(ClassLoader loader,

                                           Class<?>... interfaces) {

        if (interfaces.length > 65535) {

            throw new IllegalArgumentException("interface limit exceeded");

        }

        // If the proxy class defined by the given loader implementing

        // the given interfaces exists, this will simply return the cached copy;

        // otherwise, it will create the proxy class via the ProxyClassFactory

        return proxyClassCache.get(loader, interfaces);

    }

在Proxy類中有個屬性proxyClassCache,這是一個WeakCache類型的靜態變量。它指示了類加載器和代理類之間的映射。因此proxyClassCache的get方法用於根據類加載器來獲取Proxy類,若是已經存在則直接從cache中返回,若是沒有則建立一個映射並更新cache表。

咱們跟一下代理類的建立流程:
調用Factory類的get方法,而它又調用了ProxyClassFactory類的apply方法,最終找到下面一行代碼:

1

2

//Generate the specified proxy class.

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);

就是它,生成了代理類。

查看動態生成的代理類

經過上面的分析,咱們已經知道Proxy類動態建立代理類的流程。那建立出來的代理類究竟是什麼樣子的呢?咱們能夠經過下面的代碼,手動生成:

1

2

3

4

5

6

7

8

9

10

11

public class CodeUtil {

       public static void main(String[] args) throws IOException {

         byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", JavaCoder.class.getInterfaces());

            File file = new File("D:/aaa/TestProxyGen.class");

            FileOutputStream fos = new FileOutputStream(file);

            fos.write(classFile);

            fos.flush();

            fos.close();

          }

 }

經過反編譯工具查看生成的class文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

import model.proxy.ICoder;

public final class TestProxyGen extends Proxy

  implements ICoder

{

  private static Method m1;

  private static Method m0;

  private static Method m3;

  private static Method m2;

  public TestProxyGen(InvocationHandler paramInvocationHandler)

    throws

  {

    super(paramInvocationHandler);

  }

  public final boolean equals(Object paramObject)

    throws

  {

    try

    {

      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

    }

    catch (RuntimeException localRuntimeException)

    {

      throw localRuntimeException;

    }

    catch (Throwable localThrowable)

    {

    }

    throw new UndeclaredThrowableException(localThrowable);

  }

  public final int hashCode()

    throws

  {

    try

    {

      return ((Integer)this.h.invoke(this, m0, null)).intValue();

    }

    catch (RuntimeException localRuntimeException)

    {

      throw localRuntimeException;

    }

    catch (Throwable localThrowable)

    {

    }

    throw new UndeclaredThrowableException(localThrowable);

  }

  public final void implDemands(String paramString)

    throws

  {

    try

    {

      this.h.invoke(this, m3, new Object[] { paramString });

      return;

    }

    catch (RuntimeException localRuntimeException)

    {

      throw localRuntimeException;

    }

    catch (Throwable localThrowable)

    {

    }

    throw new UndeclaredThrowableException(localThrowable);

  }

  public final String toString()

    throws

  {

    try

    {

      return (String)this.h.invoke(this, m2, null);

    }

    catch (RuntimeException localRuntimeException)

    {

      throw localRuntimeException;

    }

    catch (Throwable localThrowable)

    {

    }

    throw new UndeclaredThrowableException(localThrowable);

  }

  static

  {

    try

    {

      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

      m3 = Class.forName("model.proxy.ICoder").getMethod("implDemands", new Class[] { Class.forName("java.lang.String") });

      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

      return;

    }

    catch (NoSuchMethodException localNoSuchMethodException)

    {

      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

    }

    catch (ClassNotFoundException localClassNotFoundException)

    {

    }

    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

  }

}

這樣,咱們就理解,爲何調用代理類的implDemands方法,回去執行中介類的invoke方法了。

相關文章
相關標籤/搜索