国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

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

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - Java動態代理分析及理解

Java動態代理分析及理解

2020-09-28 13:24Java開發-10 JAVA教程

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

Java動態代理分析及理解

代理設計模式

定義:為其他對象提供一種代理以控制對這個對象的訪問。

動態代理使用

java動態代理機制以巧妙的方式實現了代理模式的設計理念。

代理模式示例代碼

 

?
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 interface Subject 
 public void doSomething(); 
public class RealSubject implements Subject 
 public void doSomething() 
 
  System.out.println( "call doSomething()" ); 
 
public class ProxyHandler implements InvocationHandler 
 private Object proxied; 
   
 public ProxyHandler( Object proxied ) 
 
  this.proxied = proxied; 
 
   
 public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable 
 
  //在轉調具體目標對象之前,可以執行一些功能處理
 
  //轉調具體目標對象的方法
  return method.invoke( proxied, args);
  
  //在轉調具體目標對象之后,可以執行一些功能處理
 
}
?
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
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
import sun.misc.ProxyGenerator; 
import java.io.*; 
public class DynamicProxy 
 public static void main( String args[] ) 
 
  RealSubject real = new RealSubject(); 
  Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
   new Class[]{Subject.class},
   new ProxyHandler(real));
     
  proxySubject.doSomething();
  
  //write proxySubject class binary data to file 
  createProxyClassFile(); 
 
   
 public static void createProxyClassFile() 
 
  String name = "ProxySubject"
  byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } ); 
  try
  
   FileOutputStream out = new FileOutputStream( name + ".class" ); 
   out.write( data ); 
   out.close(); 
  
  catch( Exception e ) 
  
   e.printStackTrace(); 
  
 
}

動態代理內部實現

首先來看看類Proxy的代碼實現 Proxy的主要靜態變量

?
1
2
3
4
5
6
7
8
9
10
11
// 映射表:用于維護類裝載器對象到其對應的代理類緩存
private static Map loaderToCache = new WeakHashMap();
 
// 標記:用于標記一個動態代理類正在被創建中
private static Object pendingGenerationMarker = new Object();
 
// 同步表:記錄已經被創建的動態代理類類型,主要被方法 isProxyClass 進行相關的判斷
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
 
// 關聯的調用處理器引用
protected InvocationHandler h;

Proxy的構造方法

?
1
2
3
4
5
// 由于 Proxy 內部從不直接調用構造函數,所以 private 類型意味著禁止任何調用
private Proxy() {}
 
// 由于 Proxy 內部從不直接調用構造函數,所以 protected 意味著只有子類可以調用
protected Proxy(InvocationHandler h) {this.h = h;}

Proxy靜態方法newProxyInstance

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
  // 檢查 h 不為空,否則拋異常
  if (h == null) {
    throw new NullPointerException();
  }
 
  // 獲得與指定類裝載器和一組接口相關的代理類類型對象
  Class cl = getProxyClass(loader, interfaces);
 
  // 通過反射獲取構造函數對象并生成代理類實例
  try {
    Constructor cons = cl.getConstructor(constructorParams);
    return (Object) cons.newInstance(new Object[] { h });
  } 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());
  }
}

類Proxy的getProxyClass方法調用ProxyGenerator的 generateProxyClass方法產生ProxySubject.class的二進制數據:

?
1
public static byte[] generateProxyClass(final String name, Class[] interfaces)

我們可以import sun.misc.ProxyGenerator,調用 generateProxyClass方法產生binary data,然后寫入文件,最后通過反編譯工具來查看內部實現原理。 反編譯后的ProxySubject.java 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
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
import java.lang.reflect.*; 
public final class ProxySubject extends Proxy 
  implements Subject 
  private static Method m1; 
  private static Method m0; 
  private static Method m3; 
  private static Method m2; 
  public ProxySubject(InvocationHandler invocationhandler) 
  
    super(invocationhandler); 
  
  public final boolean equals(Object obj) 
  
    try
    
      return ((Boolean)super.h.invoke(this, m1, new Object[] { 
        obj 
      })).booleanValue(); 
    
    catch(Error _ex) { } 
    catch(Throwable throwable) 
    
      throw new UndeclaredThrowableException(throwable); 
    
  
  public final int hashCode() 
  
    try
    
      return ((Integer)super.h.invoke(this, m0, null)).intValue(); 
    
    catch(Error _ex) { } 
    catch(Throwable throwable) 
    
      throw new UndeclaredThrowableException(throwable); 
    
  
  public final void doSomething() 
  
    try
    
      super.h.invoke(this, m3, null); 
      return
    
    catch(Error _ex) { } 
    catch(Throwable throwable) 
    
      throw new UndeclaredThrowableException(throwable); 
    
  
  public final String toString() 
  
    try
    
      return (String)super.h.invoke(this, m2, null); 
    
    catch(Error _ex) { } 
    catch(Throwable throwable) 
    
      throw new UndeclaredThrowableException(throwable); 
    
  
  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("Subject").getMethod("doSomething", new Class[0]); 
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 
    
    catch(NoSuchMethodException nosuchmethodexception) 
    
      throw new NoSuchMethodError(nosuchmethodexception.getMessage()); 
    
    catch(ClassNotFoundException classnotfoundexception) 
    
      throw new NoClassDefFoundError(classnotfoundexception.getMessage()); 
    
  
}

ProxyGenerator內部是如何生成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
private byte[] generateClassFile() { 
 /*
  * Record that proxy methods are needed for the hashCode, equals,
  * and toString methods of java.lang.Object. This is done before
  * the methods from the proxy interfaces so that the methods from
  * java.lang.Object take precedence over duplicate methods in the
  * proxy interfaces.
  */
 addProxyMethod(hashCodeMethod, Object.class); 
 addProxyMethod(equalsMethod, Object.class); 
 addProxyMethod(toStringMethod, Object.class); 
 /*
  * Now record all of the methods from the proxy interfaces, giving
  * earlier interfaces precedence over later ones with duplicate
  * methods.
  */
 for (int i = 0; i < interfaces.length; i++) { 
   Method[] methods = interfaces[i].getMethods(); 
   for (int j = 0; j < methods.length; j++) { 
  addProxyMethod(methods[j], interfaces[i]); 
   
 
 /*
  * For each set of proxy methods with the same signature,
  * verify that the methods' return types are compatible.
  */
 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 
   checkReturnTypes(sigmethods); 
 
 /* ============================================================
  * Step 2: Assemble FieldInfo and MethodInfo structs for all of
  * fields and methods in the class we are generating.
  */
 try { 
   methods.add(generateConstructor()); 
   for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 
  for (ProxyMethod pm : sigmethods) { 
    // add static field for method's Method object 
    fields.add(new FieldInfo(pm.methodFieldName, 
   "Ljava/lang/reflect/Method;", 
    ACC_PRIVATE | ACC_STATIC)); 
    // generate code for proxy method and add it 
    methods.add(pm.generateMethod()); 
  
   
   methods.add(generateStaticInitializer()); 
 } catch (IOException e) { 
   throw new InternalError("unexpected I/O Exception"); 
 
 /* ============================================================
  * Step 3: Write the final class file.
  */
 /*
  * Make sure that constant pool indexes are reserved for the
  * following items before starting to write the final class file.
  */
 cp.getClass(dotToSlash(className)); 
 cp.getClass(superclassName); 
 for (int i = 0; i < interfaces.length; i++) { 
   cp.getClass(dotToSlash(interfaces[i].getName())); 
 
 /*
  * Disallow new constant pool additions beyond this point, since
  * we are about to write the final constant pool table.
  */
 cp.setReadOnly(); 
 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
 DataOutputStream dout = new DataOutputStream(bout); 
 try { 
   /*
    * Write all the items of the "ClassFile" structure.
    * See JVMS section 4.1.
    */
     // u4 magic; 
   dout.writeInt(0xCAFEBABE); 
     // u2 minor_version; 
   dout.writeShort(CLASSFILE_MINOR_VERSION); 
     // u2 major_version; 
   dout.writeShort(CLASSFILE_MAJOR_VERSION); 
   cp.write(dout);  // (write constant pool) 
     // u2 access_flags; 
   dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); 
     // u2 this_class; 
   dout.writeShort(cp.getClass(dotToSlash(className))); 
     // u2 super_class; 
   dout.writeShort(cp.getClass(superclassName)); 
     // u2 interfaces_count; 
   dout.writeShort(interfaces.length); 
     // u2 interfaces[interfaces_count]; 
   for (int i = 0; i < interfaces.length; i++) { 
  dout.writeShort(cp.getClass( 
    dotToSlash(interfaces[i].getName()))); 
   
     // u2 fields_count; 
   dout.writeShort(fields.size()); 
     // field_info fields[fields_count]; 
   for (FieldInfo f : fields) { 
  f.write(dout); 
   
     // u2 methods_count; 
   dout.writeShort(methods.size()); 
     // method_info methods[methods_count]; 
   for (MethodInfo m : methods) { 
  m.write(dout); 
   
       // u2 attributes_count; 
   dout.writeShort(0); // (no ClassFile attributes for proxy classes) 
 } catch (IOException e) { 
   throw new InternalError("unexpected I/O Exception"); 
 
 return bout.toByteArray();

總結

一個典型的動態代理創建對象過程可分為以下四個步驟:

1、通過實現InvocationHandler接口創建自己的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通過為Proxy類指定ClassLoader對象和一組interface創建動態代理類
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通過構造函數創建代理類實例,此時需將調用處理器對象作為參數被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
為了簡化對象創建過程,Proxy類中的newInstance方法封裝了2~4,只需兩步即可完成代理對象的創建。
生成的ProxySubject繼承Proxy類實現Subject接口,實現的Subject的方法實際調用處理器的invoke方法,而invoke方法利用反射調用的是被代理對象的的方法(Object result=method.invoke(proxied,args))

美中不足

誠然,Proxy已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏,因為它的設計注定了這個遺憾。回想一下那些動態生成的代理類的繼承關系圖,它們已經注定有一個共同的父類叫Proxy。Java的繼承機制注定了這些動態代理類們無法實現對class的動態代理,原因是多繼承在Java中本質上就行不通。有很多條理由,人們可以否定對 class代理的必要性,但是同樣有一些理由,相信支持class動態代理會更美好。接口和類的劃分,本就不是很明顯,只是到了Java中才變得如此的細化。如果只從方法的聲明及是否被定義來考量,有一種兩者的混合體,它的名字叫抽象類。實現對抽象類的動態代理,相信也有其內在的價值。此外,還有一些歷史遺留的類,它們將因為沒有實現任何接口而從此與動態代理永世無緣。如此種種,不得不說是一個小小的遺憾。但是,不完美并不等于不偉大,偉大是一種本質,Java動態代理就是佐例。

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

原文鏈接:http://blog.csdn.net/qq_37810594/article/details/71450830

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲欧洲精品成人久久奇米网 | 伊人av成人 | 久久99精品久久久 | 久久成人综合 | 99久久精品一区二区成人 | 黄色在线观看 | 成人免费视频在线观看 | 免费观看黄视频网站 | 欧美在线不卡 | 日韩欧美中文字幕在线视频 | 色网综合 | 国产一区二区三区四区在线观看 | 成人综合区 | 国产一区免费 | 国产在线不卡观看 | 精品欧美乱码久久久久久1区2区 | 黄色一区二区三区 | 成人av电影天堂 | 亚洲精品乱码久久久久久蜜桃麻豆 | 一区二区不卡视频 | 国产成人久久精品一区二区三区 | 中文字幕一区二区三区乱码在线 | 有码在线 | 亚洲日韩中文字幕一区 | 欧美成人免费在线视频 | 亚洲一区中文 | a级毛片免费高清视频 | 日韩美一级片 | 亚洲综合一区在线观看 | 亚洲精品一区在线观看 | 亚洲一区二区三区在线免费观看 | 中文字幕一区二区三区四区 | 日韩精品在线观看中文字幕 | 亚洲九九九 | 久久国产精品免费 | 黑人粗大视频 | 综合色九九 | 最近日本韩国高清免费观看 | 日韩国产欧美一区 | 精品视频久久 | 一区二区日本 |