首页 > 编程语言 >JAVA动态代理内部实现

JAVA动态代理内部实现

时间:2023-06-04 22:32:33浏览次数:50  
标签:JAVA methods interfaces Object 代理 dout new 动态 class


评:
一 代理设计模式

代理模式为目标对象提供一种代理以控制对实际对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

为了保持行为的一致性,代理类和实际委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。

 代理模式类图  

常见的代理有:
1) 远程代理(Remote proxy):对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub。 
2) 虚拟代理(Virtual proxy):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正需要的时候才创建。
3) 保护代理(Protect or Access Proxy):控制对一个对象的访问权限。
4) 智能引用(Smart Reference Proxy):提供比目标对象额外的服务和功能。

通过代理类这一中间层,能够有效控制对实际委托类对象的直接访问,也可以很好地隐藏和保护实际对,实施不同的控制策略,从而在设计上获得了更大的灵活性。
二 动态代理使用

JAVA动态代理机制以巧妙的方式实现了代理模式的设计理念。

动态代理类图


动态代理在代理ProxySubject和RealSubject之间增加了InvocationHandler,这是一种通信间接化, 增加了灵 性性,例如可以把这个中间层实现为一个框架Framework,直接通过xml文件等方式来调用RealSubject。

在普通的设计中,我们一般不会使用动态代理。但是在一些框架结构设计中,动态代理非常重要,如RMI,EJB中都使用动态代理。

view plaincopy to clipboardprint? 


 interface Subject 

 { 

 public void doSomething(); 

 } 

 class RealSubject implements Subject 

 { 

 public void doSomething() 

 { 

 System.out.println( "call doSomething()" ); 

 } 

 } 

 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); 

 } 

 }




view plaincopy to clipboardprint?

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的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:


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

我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。

反编译后的ProxySubject.java:

view plaincopy to clipboardprint? 


 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()); 

 } 

 } 

 }



通过 ProxySubject.java,我们可以看到动态代理的内部是如何实现的,并且我们可以实现自己的一个动态代理生成器。

ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。

view plaincopy to clipboardprint? 


 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();

标签:JAVA,methods,interfaces,Object,代理,dout,new,动态,class
From: https://blog.51cto.com/u_16080829/6412724

相关文章

  • 一文吃透Java并发高频面试题
    内容摘自我的学习网站:topjavaer.cn分享50道Java并发高频面试题。线程池线程池:一个管理线程的池子。为什么平时都是使用线程池创建线程,直接new一个线程不好吗?嗯,手动创建线程有两个缺点不受控风险频繁创建开销大为什么不受控?系统资源有限,每个人针对不同业务都可以手动......
  • Ubuntu22.04 安装 java JDK
    0前言1下载jdk2.配置环境变量(只修改用户配置)参考0前言Linux一般使用openjdk,其主要由Oracle提供,部分源码不可见一般学习以jdk8为基础.以下教程为jdk8的Linux-Ubuntu的安装教程1下载jdkOracle网站:https://www.oracle.com/java/technologies/downloads/#java8下载后......
  • 连网技术与网络管理2023-06-03 动态路由
    路由协议的类型主要可以分为以下三类:距离矢量协议(DistanceVectorProtocols):这类协议使用跳数(hopcount)作为衡量路径的度量标准。每个路由器仅知道自己相邻路由器的信息,并通过交换路由表来了解整个网络的路由信息。常见的距离矢量协议包括经典的RoutingInformationProtoco......
  • Java中用于发送HTTP请求的工具类
     HttpClientUtil是Java中用于发送HTTP请求的工具类,它是基于ApacheHttpClient实现的。下面是一个示例代码:importorg.apache.http.HttpEntity;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.CloseableHttpResponse;import......
  • Java中用于发送HTTP请求的工具类
     HttpClientUtil是Java中用于发送HTTP请求的工具类,它是基于ApacheHttpClient实现的。下面是一个示例代码:importorg.apache.http.HttpEntity;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.CloseableHttpResponse;import......
  • Java中用于发送HTTP请求的工具类
    ​ HttpClientUtil是Java中用于发送HTTP请求的工具类,它是基于ApacheHttpClient实现的。下面是一个示例代码:importorg.apache.http.HttpEntity;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.CloseableHttpResponse;......
  • Java中用于发送HTTP请求的工具类
    ​ HttpClientUtil是Java中用于发送HTTP请求的工具类,它是基于ApacheHttpClient实现的。下面是一个示例代码:importorg.apache.http.HttpEntity;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.CloseableHttpResponse;......
  • Java中HttpClientUtil工具类
     HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集*/......
  • Java中HttpClientUtil工具类
     HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集*/......
  • Java中HttpClientUtil工具类
    ​ HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集......