激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

服務器之家 - 編程語言 - Java教程 - Java 動態代理深入理解

Java 動態代理深入理解

2020-08-29 12:02Java教程網 Java教程

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

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

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

代理模式

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

靜態代理

讓我們先來看一段代碼:

?
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類實現Subject接口并實現其中的兩個方法,到這里肯定是沒問題的。

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

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

動態代理

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

下面我把上面例子的靜態代理改成動態代理,我們來看一下區別:

?
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方法的話,只有第二行和之前的靜態代理不同,同樣是生成一個subjectProxy代理對象,只是生成的代碼不同了。靜態代理是直接new 一個SubjectImplProxy的實例,而動態代理則調用了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有一個參數為InvocationHandler的構造函數
    return cons.newInstance(new Object[] { h });  //這里通過構造函數將我們自己定義的InvocationHandler的子類傳到代理類的實例里,當我們調用代理類的任何方法時,實際上都會調用我們定義的InvocationHandler子類重寫的invoke()函數
  } 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文件中,一個類保存的接口數量是用2個字節來表示的,因此java中一個類最多可以實現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里,也就是實現了相同interfaces的代理類只會創建加載一次
 
    /*
     * 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,否則判斷根據pendingGenerationMarker判斷是否有其它線程正在生成當前的代理類,如果有則cache.wait()等待,如果沒有則創建。
    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型數字,從0開始累積,每次生成新的代理類就+1,從這里也能看出生成的動態代理類的數量不能超過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為類名的,實現了Interfaces里所有接口的類的字節碼
        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.
       */
       //創建成功,則將cache中該key的pendingGenerationMarker替換為實際的代理類的弱引用,否則也要清除pendingGenerationMarker標記;不管是否成功,都要執行cache.notifyAll(),讓其它要創建相同代理類并且執行了cache.wait()的線程恢復執行。
      synchronized (cache) {
        if (proxyClass != null) {
          cache.put(key, new WeakReference<Class<?>>(proxyClass));
        } else {
          cache.remove(key);
        }
        cache.notifyAll();
      }
    }
    return proxyClass; //最后返回代理類Class
  }

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

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

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

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

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

現在我們知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功強轉成Subject類型來調用接口中定義的方法了,那么在調用方法后,代理類實例怎么進行處理的呢,這就需要看一下代理類的源碼了。但是代理類是程序動態生成字節碼加載的,怎么看源碼呢?沒關系,可以在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);
  }
 }
}

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

總結

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

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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 毛片在线视频免费观看 | 日日操夜 | 青青草最新网址 | 中文在线观看www | 久久96国产精品久久久 | 国产精品视频中文字幕 | 国产日本在线播放 | 3344永久免费 | 欧美成年性h版影视中文字幕 | 国产精品亚洲欧美 | 99欧美视频 | 国产精品视频yy9299一区 | 2019天天干夜夜操 | 日韩精品免费看 | 久久久久久久久久久久久久久久久久 | 亚洲视频在线观看免费 | 亚洲一区二区三区视频免费 | av在线播放免费观看 | 亚洲免费在线视频 | 日韩视频区 | 中文字幕在线资源 | 久久久久久久.comav | 一级大片久久 | 91香焦视频 | 久久影院在线观看 | 亚洲免费视频一区 | v片在线看 | 亚洲第一成人久久网站 | 国产亚洲精品久久777777 | 在线看小早川怜子av | 欧洲精品久久久久69精品 | 欧美成人一区二区三区电影 | 国产porn在线 | 成人福利在线 | 国产99一区二区 | 国产精品亚洲yourport | 国产视频在线观看一区二区三区 | 日韩av在线影院 | 国产精品免费一区二区三区都可以 | 久久人人做 | 国产精品jk白丝蜜臀av软件 |