一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java 動態(tài)代理深入理解

Java 動態(tài)代理深入理解

2020-08-29 12:02Java教程網(wǎng) Java教程

這篇文章主要介紹了Java 動態(tài)代理深入理解的相關資料,需要的朋友可以參考下

要想了解Java動態(tài)代理,首先要了解什么叫做代理,熟悉設計模式的朋友一定知道在Gof總結的23種設計模式中,有一種叫做代理(Proxy)的對象結構型模式,動態(tài)代理中的代理,指的就是這種設計模式。

在我看來所謂的代理模式,和23種設計模式中的“裝飾模式”是一個東西。23種設計模式中將它們作為兩種模式,網(wǎng)上也有些文章講這兩種模式的異同,從細節(jié)來看,確實可以人為地區(qū)分這兩種模式,但是抽象到一定高度后,我認為這兩種模式是完全一樣的。因此學會了代理模式,也就同時掌握了裝飾模式。

代理模式

代理模式簡單來說,就是對一個對象進行包裝,包裝后生成的對象具有和原對象一樣的方法列表,但是每個方法都可以是被包裝過的。

靜態(tài)代理

讓我們先來看一段代碼:

?
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
package common;
 
public class Test {
  static interface Subject{
    void sayHi();
    void sayHello();
  }
 
  static class SubjectImpl implements Subject{
 
    @Override
    public void sayHi() {
      System.out.println("hi");
    }
 
    @Override
    public void sayHello() {
      System.out.println("hello");
    }
  }
 
  static class SubjectImplProxy implements Subject{
    private Subject target;
 
    public SubjectImplProxy(Subject target) {
      this.target=target;
    }
 
    @Override
    public void sayHi() {
      System.out.print("say:");
      target.sayHi();
    }
 
    @Override
    public void sayHello() {
      System.out.print("say:");
      target.sayHello();
    }
  }
 
  public static void main(String[] args) {
    Subject subject=new SubjectImpl();
    Subject subjectProxy=new SubjectImplProxy(subject);
    subjectProxy.sayHi();
    subjectProxy.sayHello();
  }
}

這段代碼中首先定義了一個Subject接口,接口中有兩個方法。

然后定義了SubjectImpl類實現(xiàn)Subject接口并實現(xiàn)其中的兩個方法,到這里肯定是沒問題的。

現(xiàn)在再定義一個SubjuectImplProxy類,也實現(xiàn)Subject接口。這個SubjectImplProxy類的作用是包裝SubjectImpl類的實例,它的內部定義一個變量target來保存一個SubjectImpl的實例。SubjectImplProxy也實現(xiàn)了接口規(guī)定的兩個方法,并且在它的實現(xiàn)版本中,都調用了SubjectImpl的實現(xiàn),但是又添加了自己的處理邏輯。

相信這段代碼不難理解,它通過對SubjectImpl進行包裝,達到了給輸出內容添加前綴的功能。這種代理方式叫做靜態(tài)代理。

動態(tài)代理

從上面的演示中我們不難看出靜態(tài)代理的缺點:我們對SubjectImpl的兩個方法,是進行的相同的包裝,但是卻要在SubjectImplProxy里把相同的包裝邏輯寫兩次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實現(xiàn),盡管SubjectImplProxy對所有方法的包裝可能都是一樣的。

下面我把上面例子的靜態(tài)代理改成動態(tài)代理,我們來看一下區(qū)別:

?
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
package common;
 
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class Test {
  static interface Subject{
    void sayHi();
    void sayHello();
  }
 
  static class SubjectImpl implements Subject{
 
    @Override
    public void sayHi() {
      System.out.println("hi");
    }
 
    @Override
    public void sayHello() {
      System.out.println("hello");
    }
  }
 
  static class ProxyInvocationHandler implements InvocationHandler{
    private Subject target;
    public ProxyInvocationHandler(Subject target) {
      this.target=target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.print("say:");
      return method.invoke(target, args);
    }
 
  }
 
  public static void main(String[] args) {
    Subject subject=new SubjectImpl();
    Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
    subjectProxy.sayHi();
    subjectProxy.sayHello();
 
  }
}

只看main方法的話,只有第二行和之前的靜態(tài)代理不同,同樣是生成一個subjectProxy代理對象,只是生成的代碼不同了。靜態(tài)代理是直接new 一個SubjectImplProxy的實例,而動態(tài)代理則調用了java.lang.reflect.Proxy.newProxyInstance()方法,我們來看一下這個方法的源碼:

 

?
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
public static Object newProxyInstance(ClassLoader loader,
                   Class<?>[] interfaces,
                   InvocationHandler h)
  throws IllegalArgumentException
{
  if (h == null) {
    throw new NullPointerException();
  }
 
  /*
   * Look up or generate the designated proxy class.
   */
  Class<?> cl = getProxyClass(loader, interfaces);  //獲取代理類的Class
 
  /*
   * Invoke its constructor with the designated invocation handler.
   */
  try {
    Constructor cons = cl.getConstructor(constructorParams);  //constructorParams是寫死的:{ InvocationHandler.class },上邊返回的代理類Class一定是extends Proxy的,而Proxy有一個參數(shù)為InvocationHandler的構造函數(shù)
    return cons.newInstance(new Object[] { h });  //這里通過構造函數(shù)將我們自己定義的InvocationHandler的子類傳到代理類的實例里,當我們調用代理類的任何方法時,實際上都會調用我們定義的InvocationHandler子類重寫的invoke()函數(shù)
  } catch (NoSuchMethodException e) {
    throw new InternalError(e.toString());
  } catch (IllegalAccessException e) {
    throw new InternalError(e.toString());
  } catch (InstantiationException e) {
    throw new InternalError(e.toString());
  } catch (InvocationTargetException e) {
    throw new InternalError(e.toString());
  }
}

上面的 Class<?> cl = getProxyClass(loader, interfaces);  調用的getProxyClass方法:

?
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
public static Class<?> getProxyClass(ClassLoader loader,
                     Class<?>... interfaces)
    throws IllegalArgumentException
  {
    if (interfaces.length > 65535) {  //因為在class文件中,一個類保存的接口數(shù)量是用2個字節(jié)來表示的,因此java中一個類最多可以實現(xiàn)65535個接口
      throw new IllegalArgumentException("interface limit exceeded");
    }
 
    Class<?> proxyClass = null;
 
    /* collect interface names to use as key for proxy class cache */
    String[] interfaceNames = new String[interfaces.length];
 
    // for detecting duplicates
    Set<Class<?>> interfaceSet = new HashSet<>();
     //驗證interfaces里的接口是否能被類加載器加載,是否是接口,是否有重復的 
    for (int i = 0; i < interfaces.length; i++) {
      /*
       * Verify that the class loader resolves the name of this
       * interface to the same Class object.
       */
      String interfaceName = interfaces[i].getName();
      Class<?> interfaceClass = null;
      try {
        interfaceClass = Class.forName(interfaceName, false, loader);
      } catch (ClassNotFoundException e) {
      }
      if (interfaceClass != interfaces[i]) {
        throw new IllegalArgumentException(
          interfaces[i] + " is not visible from class loader");
      }
 
      /*
       * Verify that the Class object actually represents an
       * interface.
       */
      if (!interfaceClass.isInterface()) {
        throw new IllegalArgumentException(
          interfaceClass.getName() + " is not an interface");
      }
 
      /*
       * Verify that this interface is not a duplicate.
       */
      if (interfaceSet.contains(interfaceClass)) {
        throw new IllegalArgumentException(
          "repeated interface: " + interfaceClass.getName());
      }
      interfaceSet.add(interfaceClass);
 
      interfaceNames[i] = interfaceName;
    }
 
    /*
     * Using string representations of the proxy interfaces as
     * keys in the proxy class cache (instead of their Class
     * objects) is sufficient because we require the proxy
     * interfaces to be resolvable by name through the supplied
     * class loader, and it has the advantage that using a string
     * representation of a class makes for an implicit weak
     * reference to the class.
     */
    List<String> key = Arrays.asList(interfaceNames);  //使用interfaces列表作為key緩存在cache里,也就是實現(xiàn)了相同interfaces的代理類只會創(chuàng)建加載一次
 
    /*
     * Find or create the proxy class cache for the class loader.
     */
    Map<List<String>, Object> cache;
    synchronized (loaderToCache) {
      cache = loaderToCache.get(loader);
      if (cache == null) {
        cache = new HashMap<>();
        loaderToCache.put(loader, cache);
      }
      /*
       * This mapping will remain valid for the duration of this
       * method, without further synchronization, because the mapping
       * will only be removed if the class loader becomes unreachable.
       */
    }
 
    /*
     * Look up the list of interfaces in the proxy class cache using
     * the key. This lookup will result in one of three possible
     * kinds of values:
     *   null, if there is currently no proxy class for the list of
     *     interfaces in the class loader,
     *   the pendingGenerationMarker object, if a proxy class for the
     *     list of interfaces is currently being generated,
     *   or a weak reference to a Class object, if a proxy class for
     *     the list of interfaces has already been generated.
     */
     //看看緩存里有沒有,如果有就直接取出來然后return,否則判斷根據(jù)pendingGenerationMarker判斷是否有其它線程正在生成當前的代理類,如果有則cache.wait()等待,如果沒有則創(chuàng)建。
    synchronized (cache) {
      /*
       * Note that we need not worry about reaping the cache for
       * entries with cleared weak references because if a proxy class
       * has been garbage collected, its class loader will have been
       * garbage collected as well, so the entire cache will be reaped
       * from the loaderToCache map.
       */
      do {
        Object value = cache.get(key);
        if (value instanceof Reference) {
          proxyClass = (Class<?>) ((Reference) value).get();
        }
        if (proxyClass != null) {
          // proxy class already generated: return it
          return proxyClass;
        } else if (value == pendingGenerationMarker) {
          // proxy class being generated: wait for it
          try {
            cache.wait();
          } catch (InterruptedException e) {
            /*
             * The class generation that we are waiting for should
             * take a small, bounded time, so we can safely ignore
             * thread interrupts here.
             */
          }
          continue;
        } else {
          /*
           * No proxy class for this list of interfaces has been
           * generated or is being generated, so we will go and
           * generate it now. Mark it as pending generation.
           */
          cache.put(key, pendingGenerationMarker);
          break;
        }
      } while (true);
    }
     //確認要生成的代理類所屬的包,如果interfaces里所有接口都是public的,代理類所屬包就是默認包;如果有interface不是public,那么所有不是public的interface必須在一個包里否則報錯。
    try {
      String proxyPkg = null;   // package to define proxy class in
 
      /*
       * Record the package of a non-public proxy interface so that the
       * proxy class will be defined in the same package. Verify that
       * all non-public proxy interfaces are in the same package.
       */
      for (int i = 0; i < interfaces.length; i++) {
        int flags = interfaces[i].getModifiers();
        if (!Modifier.isPublic(flags)) {
          String name = interfaces[i].getName();
          int n = name.lastIndexOf('.');
          String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
          if (proxyPkg == null) {
            proxyPkg = pkg;
          } else if (!pkg.equals(proxyPkg)) {
            throw new IllegalArgumentException(
              "non-public interfaces from different packages");
          }
        }
      }
 
      if (proxyPkg == null) {   // if no non-public proxy interfaces,
        proxyPkg = "";     // use the unnamed package
      }
 
      {
        /*
         * Choose a name for the proxy class to generate.
         */
        long num;
        synchronized (nextUniqueNumberLock) {
          num = nextUniqueNumber++;
        }
        String proxyName = proxyPkg + proxyClassNamePrefix + num;  //生成代理類的名字,proxyPkg是上面確定下來的代理類所在的包名,proxyClassNamePrefix是寫死的字符串“$Proxy”,num是一個全局唯一的long型數(shù)字,從0開始累積,每次生成新的代理類就+1,從這里也能看出生成的動態(tài)代理類的數(shù)量不能超過Long.maxValue
        /*
         * Verify that the class loader hasn't already
         * defined a class with the chosen name.
         */
 
        /*
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
          proxyName, interfaces);  //生成一個以proxyName為類名的,實現(xiàn)了Interfaces里所有接口的類的字節(jié)碼
        try {
          proxyClass = defineClass0(loader, proxyName,
            proxyClassFile, 0, proxyClassFile.length);  //加載生成的類
        } catch (ClassFormatError e) {
          /*
           * A ClassFormatError here means that (barring bugs in the
           * proxy class generation code) there was some other
           * invalid aspect of the arguments supplied to the proxy
           * class creation (such as virtual machine limitations
           * exceeded).
           */
          throw new IllegalArgumentException(e.toString());
        }
      }
      // add to set of all generated proxy classes, for isProxyClass
      proxyClasses.put(proxyClass, null);
 
    } finally {
      /*
       * We must clean up the "pending generation" state of the proxy
       * class cache entry somehow. If a proxy class was successfully
       * generated, store it in the cache (with a weak reference);
       * otherwise, remove the reserved entry. In all cases, notify
       * all waiters on reserved entries in this cache.
       */
       //創(chuàng)建成功,則將cache中該key的pendingGenerationMarker替換為實際的代理類的弱引用,否則也要清除pendingGenerationMarker標記;不管是否成功,都要執(zhí)行cache.notifyAll(),讓其它要創(chuàng)建相同代理類并且執(zhí)行了cache.wait()的線程恢復執(zhí)行。
      synchronized (cache) {
        if (proxyClass != null) {
          cache.put(key, new WeakReference<Class<?>>(proxyClass));
        } else {
          cache.remove(key);
        }
        cache.notifyAll();
      }
    }
    return proxyClass; //最后返回代理類Class
  }

到這里,我們已經把動態(tài)代理的java源代碼都解析完了,現(xiàn)在思路就很清晰了:

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法簡單來說執(zhí)行了以下操作:

1.生成一個實現(xiàn)了參數(shù)interfaces里所有接口且繼承了Proxy的代理類的字節(jié)碼,然后用參數(shù)里的classLoader加載這個代理類。

2.使用代理類父類的構造函數(shù) Proxy(InvocationHandler h)來創(chuàng)造一個代理類的實例,將我們自定義的InvocationHandler的子類傳入。

3.返回這個代理類實例,因為我們構造的代理類實現(xiàn)了interfaces(也就是我們程序中傳入的subject.getClass().getInterfaces())里的所有接口,因此返回的代理類可以強轉成Subject類型來調用接口中定義的方法。

現(xiàn)在我們知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功強轉成Subject類型來調用接口中定義的方法了,那么在調用方法后,代理類實例怎么進行處理的呢,這就需要看一下代理類的源碼了。但是代理類是程序動態(tài)生成字節(jié)碼加載的,怎么看源碼呢?沒關系,可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”),這樣就會把生成的代理類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
110
111
112
113
114
115
116
package common;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
 
public final class $Proxy0 extends Proxy
 implements Test.Subject
{
 private static Method m4;
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;
 
 static
 {
   try {
     m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
     m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
     m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]);
     m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
     m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
 }
 
 public $Proxy0(InvocationHandler paramInvocationHandler)
 {
  super(paramInvocationHandler);
 }
 
 public final void sayHello()
 {
  try
  {
   this.h.invoke(this, m4, null);
   return;
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final boolean equals(Object paramObject)
 {
  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 void sayHi()
 {
  try
  {
   this.h.invoke(this, m3, null);
   return;
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final int hashCode()
 {
  try
  {
   return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final String toString()
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
}

我們可以看到代理類內部實現(xiàn)比較簡單,在調用每個代理類每個方法的時候,都用反射去調h的invoke方法(也就是我們自定義的InvocationHandler的子類中重寫的invoke方法),用參數(shù)傳遞了代理類實例、接口方法、調用參數(shù)列表,這樣我們在重寫的invoke方法中就可以實現(xiàn)對所有方法的統(tǒng)一包裝了。

總結

動態(tài)代理相對于靜態(tài)代理在使用上的優(yōu)點主要是能夠對一個對象的所有方法進行統(tǒng)一包裝,而且后期被代理的類添加方法的時候動態(tài)代理類不需要改動。

缺點是要求被代理的類必須實現(xiàn)了接口,因為動態(tài)代理類在實現(xiàn)的時候繼承了Proxy類,java不支持多繼承,因此動態(tài)代理類只能根據(jù)接口來定義方法。

最后動態(tài)代理之所以叫做動態(tài)代理是因為java在實現(xiàn)動態(tài)代理的時候,動態(tài)代理類是在運行時動態(tài)生成和加載的,相對的,靜態(tài)代理類和其他普通類一下,在類加載階段就加載了。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩一区二区三区免费不卡 | 日韩亚洲国产激情在线观看 | juliaann大战两个黑人 | 亚洲国产精品久久网午夜 | 日本一区二区不卡久久入口 | 国产成人高清精品免费5388密 | 国产精品麻豆 | 久久久久伊人 | 禁忌4中文 | 亚洲久操 | 全肉一女n男np高h乳 | 91久久综合九色综合欧美98 | 高跟翘臀老师后进式视频 | 天天做天天爱天天爽综合区 | 日日本老女人 | 护士让我吃奶我扒她奶 | 2020精品极品国产色在线观看 | 午夜香蕉成视频人网站高清版 | 欧美男同video | 毛片免费毛片一级jjj毛片 | 擦逼视频 | 9420高清完整版在线观看国语 | 美女口述又粗又大感觉 | 国产综合图区 | 亚洲AV无码乱码在线观看浪潮 | 手机在线观看伦理片 | 国产精品微拍 | www.色婷婷.com | 午夜无码国产理论在线 | a级特黄毛片免费观看 | 日b在线观看 | 亚洲图片一区二区三区 | 成人啪精品视频免费网站 | 网www天堂资源在线 王淑兰与铁柱全文免费阅读 | pron在线观看 | 国产精品免费拍拍拍 | 好吊日在线 | 亚洲精品青青草原avav久久qv | 狠狠色 | 青青久久精品国产免费看 | 刺客女仆 |