JAVA 动态代理(proxy)的实现和源码分析

https://i-blog.csdnimg.cn/blog_migrate/9cf950b161ffd6e53521492968bd213c.png


JDK动态代理(proxy)可以在运行时创建一个实现一组给定接口的新类。但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功能更加强大的动态代理技术—— CGLIB。Spring里会自动在JDK的代理和CGLIB之间切换,同时我们也可以强制Spring使用CGLIB。

闲话少说,下面先用实例介绍使用方式,接着从proxy类源码角度分析实现过程:

1.JDK动态代理实例

(1)动态代理首先提供一个调度处理器接口(Invocationhandler),该接口实例封装了我们要代理的对象实例的数据。

  1. public class TranceHander implements InvocationHandler{  
  2.     private Object tObject;  
  3.     public TranceHander(Object t){  
  4.         tObject=t;  
  5.     }  
  6.     @Override  
  7.     public Object invoke(Object proxy, Method method, Object[] args)  
  8.             throws Throwable {  
  9.         // TODO Auto-generated method stub  
  10.         System.out.println("被代理对象:"+tObject);  
  11.         System.out.println("方法:"+method.getName());  
  12.         return method.invoke(tObject,args);//方法反射  
  13.     }  
  14. }  

(2)使用Proxy类的newProxyInstance方法创建代理对象或getProxyClass方法获得代理类Class。

测试mian方法中,写了一个代理类,从1-1000的整数数组中使用二分查找随机查找一个整数,实现对象比较和toString接口代理

  1. public class ProxyTest {  
  2.   
  3.     /** 
  4.      * @param args 
  5.      */  
  6.     public static void main(String[] args) {  
  7.         // TODO Auto-generated method stub  
  8.         Object[] elements=new Object[1000];  
  9.         for(int i=0;i<elements.length;i++){  
  10.             Integer value=i+1;//被代理对象  
  11.             InvocationHandler handler=new TranceHander(value);//构建调度处理器  
  12.             Object proxy=Proxy.newProxyInstance(nullnew Class[]{Comparable.class}, handler);  
  13.             elements[i]=proxy;  
  14.         }  
  15.         Integer key=new Random().nextInt(1000)+1;  
  16.         int res=Arrays.binarySearch(elements, key);  
  17.         if(res>=0){  
  18.             System.out.println(elements[res].toString());  
  19.         }  
  20.         System.out.println("查找Key:"+key);  
  21.     }  
  22. }  

运行结果:



2.JDK源码实现原理分析

到这里我们发现,我们使用代理对象调用接口时候均调用TranceHander对象的invoker方法,使用Method的类的反射机制执行被代理对象的实现接口方法。
到这里我们得思考:
1. 这个代理对象是由谁且怎么生成的?
2. invoke方法是怎么调用的?
3. invoke和add方法有什么对应关系?
4. 生成的代理对象是什么样子的?
下面我引入了OPEN JDK源码 点击打开链接 ,查看了一下午,终于看明白了 大笑

(1)代理对象生成:
  1. Object proxy=Proxy.newProxyInstance(nullnew Class[]{Comparable.class}, handler);  

(2)查看Proxy类的newProxyInstance方法
从生成对象方法中,我们看到三个关键的地方:
  • Class<?> cl = getProxyClass0(loader, interfaces);//得到代理类
  • final Constructor<?> cons = cl.getConstructor(constructorParams);   
  • newInstance(cons, ih);//将InvocationHandler h传入代理对象中
  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                       Class<?>[] interfaces,  
  3.                                       InvocationHandler h)  
  4.     throws IllegalArgumentException  
  5. {  
  6.     if (h == null) {  
  7.         throw new NullPointerException();  
  8.     }  
  9.   
  10.     final SecurityManager sm = System.getSecurityManager();  
  11.     if (sm != null) {  
  12.         checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);  
  13.     }  
  14.   
  15.     /* 
  16.      * Look up or generate the designated proxy class. 
  17.      */  
  18.     Class<?> cl = getProxyClass0(loader, interfaces);  
  19.   
  20.     /* 
  21.      * Invoke its constructor with the designated invocation handler. 
  22.      */  
  23.     try {  
  24.         final Constructor<?> cons = cl.getConstructor(constructorParams);  
  25.         final InvocationHandler ih = h;  
  26.         if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {  
  27.             // create proxy instance with doPrivilege as the proxy class may  
  28.             // implement non-public interfaces that requires a special permission  
  29.             return AccessController.doPrivileged(new PrivilegedAction<Object>() {  
  30.                 public Object run() {  
  31.                     return newInstance(cons, ih);  
  32.                 }  
  33.             });  
  34.         } else {  
  35.             return newInstance(cons, ih);  
  36.         }  
  37.     } catch (NoSuchMethodException e) {  
  38.         throw new InternalError(e.toString());  
  39.     }  
  40. }  

(3)生成Proxy类,查看 getProxyClass0方法,

这个方法有点长,抽取关键的地方:
1.先抽取被代理类实现的接口Interface,检测是否符合要求
  1. /* collect interface names to use as key for proxy class cache */  
  2. String[] interfaceNames = new String[interfaces.length];  
  3.   
  4. // for detecting duplicates  
  5. Set<Class<?>> interfaceSet = new HashSet<>();  
  6.   
  7. for (int i = 0; i < interfaces.length; i++) {  
  8.     /* 
  9.      * Verify that the class loader resolves the name of this 
  10.      * interface to the same Class object. 
  11.      */  
  12.     String interfaceName = interfaces[i].getName();  
  13.     Class<?> interfaceClass = null;  
  14.     try {  
  15.         interfaceClass = Class.forName(interfaceName, false, loader);  
  16.     } catch (ClassNotFoundException e) {  
  17.     }  
  18.     if (interfaceClass != interfaces[i]) {  
  19.         throw new IllegalArgumentException(  
  20.             interfaces[i] + " is not visible from class loader");  
  21.     }  
  22.   
  23.     /* 
  24.      * Verify that the Class object actually represents an 
  25.      * interface. 
  26.      */  
  27.     if (!interfaceClass.isInterface()) {  
  28.         throw new IllegalArgumentException(  
  29.             interfaceClass.getName() + " is not an interface");  
  30.     }  
  31.   
  32.     /* 
  33.      * Verify that this interface is not a duplicate. 
  34.      */  
  35.     if (interfaceSet.contains(interfaceClass)) {  
  36.         throw new IllegalArgumentException(  
  37.             "repeated interface: " + interfaceClass.getName());  
  38.     }  
  39.     interfaceSet.add(interfaceClass);  
  40.   
  41.     interfaceNames[i] = interfaceName;  
  42. }  

2.查看代理对象缓存中是否有我们要创建的代理类,如果有,直接获取;没有则创建

如果有则  return  proxyClassCache.get(loader, interfaces); 
   没有则创建,先定义代理类的包名,如果所有的接口都是公共的,则默认将创建的类放在String PROXY_PACKAGE = "com.sun.proxy";中,若果存在
非public接口则所有的非public接口必须在同一包下,代理类也放在此包下,否则系统抛异常。
   
  1. <span style="white-space:pre">    </span>   for (int i = 0; i < interfaces.length; i++) {  
  2.                int flags = interfaces[i].getModifiers();  
  3.                if (!Modifier.isPublic(flags)) {  
  4.                    String name = interfaces[i].getName();  
  5.                    int n = name.lastIndexOf('.');  
  6.                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  
  7.                    if (proxyPkg == null) {  
  8.                        proxyPkg = pkg;  
  9.                    } else if (!pkg.equals(proxyPkg)) {  
  10.                        throw new IllegalArgumentException(  
  11.                            "non-public interfaces from different packages");  
  12.                    }  
  13.                }  
  14.            }  
  15.   
  16.            if (proxyPkg == null) {  
  17.                // if no non-public proxy interfaces, use com.sun.proxy package  
  18.                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  
  19.            }  

接下来创建类,和将字节码写入类文件中:
  1. byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);  
  2. proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);  
  3. return proxyClass;  


(4)创建代理实例,将
  • newInstance(cons, ih);//将InvocationHandler h传入代理对象中

   看完源码,我还是有点困惑,直到我看到生成的代理类文件源码后,豁然开朗。其实主要一步就在于生成类
的过程,类文件中建立被代理对象接口的invoke调用,话不多说看源码(我直接在测试用例调用的ProxyGenerator.generateProxyClass( proxyName, interfaces)接口生成的)
  1. public static void main(String[] args) {  
  2.     // TODO Auto-generated method stub  
  3.     Integer value=1;  
  4.     InvocationHandler handler=new TranceHander(value);//构建调度处理器  
  5.     Object proxy=Proxy.newProxyInstance(null, Integer.class.getInterfaces(), handler);  
  6.     String path = "D://aaa.class";    
  7.     byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",Integer.class.getInterfaces());   
  8.        FileOutputStream out = null;    
  9.    
  10.        try {    
  11.            out = new FileOutputStream(path);    
  12.            out.write(classFile);    
  13.            out.flush();    
  14.        } catch (Exception e) {    
  15.            e.printStackTrace();    
  16.        } finally {    
  17.            try {    
  18.                out.close();    
  19.            } catch (IOException e) {    
  20.                e.printStackTrace();    
  21.            }    
  22.        }    
  23. }  

接着看class文件的反编译源码吧,看大家都明白了吧,直接用的Method的反射机制实现的代理,equal、hascode、toString方法是所有类都有的,compareTo则是被代理对象的实现接口:
  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4. import java.lang.reflect.UndeclaredThrowableException;  
  5.   
  6. public final class $Proxy0 extends Proxy  
  7.   implements Comparable  
  8. {  
  9.   private static Method m3;  
  10.   private static Method m1;  
  11.   private static Method m0;  
  12.   private static Method m2;  
  13.   
  14.   public $Proxy0(InvocationHandler paramInvocationHandler)  
  15.     throws   
  16.   {  
  17.     super(paramInvocationHandler);  
  18.   }  
  19.   
  20.   public final int compareTo(Object paramObject)  
  21.     throws   
  22.   {  
  23.     try  
  24.     {  
  25.       return ((Integer)this.h.invoke(this, m3, new Object[] { paramObject })).intValue();  
  26.     }  
  27.     catch (Error|RuntimeException localError)  
  28.     {  
  29.       throw localError;  
  30.     }  
  31.     catch (Throwable localThrowable)  
  32.     {  
  33.       throw new UndeclaredThrowableException(localThrowable);  
  34.     }  
  35.   }  
  36.   
  37.   public final boolean equals(Object paramObject)  
  38.     throws   
  39.   {  
  40.     try  
  41.     {  
  42.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
  43.     }  
  44.     catch (Error|RuntimeException localError)  
  45.     {  
  46.       throw localError;  
  47.     }  
  48.     catch (Throwable localThrowable)  
  49.     {  
  50.       throw new UndeclaredThrowableException(localThrowable);  
  51.     }  
  52.   }  
  53.   
  54.   public final int hashCode()  
  55.     throws   
  56.   {  
  57.     try  
  58.     {  
  59.       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
  60.     }  
  61.     catch (Error|RuntimeException localError)  
  62.     {  
  63.       throw localError;  
  64.     }  
  65.     catch (Throwable localThrowable)  
  66.     {  
  67.       throw new UndeclaredThrowableException(localThrowable);  
  68.     }  
  69.   }  
  70.   
  71.   public final String toString()  
  72.     throws   
  73.   {  
  74.     try  
  75.     {  
  76.       return (String)this.h.invoke(this, m2, null);  
  77.     }  
  78.     catch (Error|RuntimeException localError)  
  79.     {  
  80.       throw localError;  
  81.     }  
  82.     catch (Throwable localThrowable)  
  83.     {  
  84.       throw new UndeclaredThrowableException(localThrowable);  
  85.     }  
  86.   }  
  87.   
  88.   static  
  89.   {  
  90.     try  
  91.     {  
  92.       m3 = Class.forName("java.lang.Comparable").getMethod("compareTo"new Class[] { Class.forName("java.lang.Object") });  
  93.       m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[] { Class.forName("java.lang.Object") });  
  94.       m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);  
  95.       m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);  
  96.       return;  
  97.     }  
  98.     catch (NoSuchMethodException localNoSuchMethodException)  
  99.     {  
  100.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
  101.     }  
  102.     catch (ClassNotFoundException localClassNotFoundException)  
  103.     {  
  104.       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  
  105.     }  
  106.   }  
  107. }  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值