JDK的动态代理原理

首先,invoke方法的完整形式如下: 
Java代码   收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  2.     {  
  3.   
  4.         method.invoke(obj, args);  
  5.   
  6.         return null;  
  7.     }  

        首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下: 
Java代码   收藏代码
  1. A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.  

        由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。 

        为了方便说明,这里写一个简单的例子来实现动态代理。 

       
Java代码   收藏代码
  1. //抽象角色(动态代理只能代理接口)  
  2. public interface Subject {  
  3.       
  4.     public void request();  
  5. }  

Java代码   收藏代码
  1. //真实角色:实现了Subject的request()方法  
  2. public class RealSubject implements Subject{  
  3.       
  4.     public void request(){  
  5.         System.out.println("From real subject.");  
  6.     }  
  7. }  

Java代码   收藏代码
  1. //实现了InvocationHandler  
  2. public class DynamicSubject implements InvocationHandler  
  3. {  
  4.     private Object obj;  
  5.   
  6.     public DynamicSubject()  
  7.     {  
  8.     }  
  9.   
  10.     public DynamicSubject(Object obj)  
  11.     {  
  12.         this.obj = obj;  
  13.     }  
  14.   
  15.     //这个方法不是我们显示的去调用  
  16.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  17.     {  
  18.         System.out.println("before calling " + method);  
  19.   
  20.         method.invoke(obj, args);  
  21.   
  22.         System.out.println("after calling " + method);  
  23.   
  24.         return null;  
  25.     }  
  26.   
  27. }  

Java代码   收藏代码
  1. //客户端:生成代理实例,并调用了request()方法  
  2. public class Client {  
  3.   
  4.     public static void main(String[] args) throws Throwable{  
  5.         // TODO Auto-generated method stub  
  6.   
  7.         Subject rs=new RealSubject();//这里指定被代理类  
  8.         InvocationHandler ds=new DynamicSubject(rs);  
  9.         Class<?> cls=rs.getClass();  
  10.           
  11.         //以下是一次性生成代理  
  12.           
  13.         Subject subject=(Subject) Proxy.newProxyInstance(  
  14.                 cls.getClassLoader(),cls.getInterfaces(), ds);  
  15.           
  16.         //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  
  17.         System.out.println(subject instanceof Proxy);  
  18.           
  19.         //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  
  20.         System.out.println("subject的Class类是:"+subject.getClass().toString());  
  21.           
  22.         System.out.print("subject中的属性有:");  
  23.           
  24.         Field[] field=subject.getClass().getDeclaredFields();  
  25.         for(Field f:field){  
  26.             System.out.print(f.getName()+", ");  
  27.         }  
  28.           
  29.         System.out.print("\n"+"subject中的方法有:");  
  30.           
  31.         Method[] method=subject.getClass().getDeclaredMethods();  
  32.           
  33.         for(Method m:method){  
  34.             System.out.print(m.getName()+", ");  
  35.         }  
  36.           
  37.         System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());  
  38.           
  39.         System.out.print("\n"+"subject实现的接口是:");  
  40.           
  41.         Class<?>[] interfaces=subject.getClass().getInterfaces();  
  42.           
  43.         for(Class<?> i:interfaces){  
  44.             System.out.print(i.getName()+", ");  
  45.         }  
  46.   
  47.         System.out.println("\n\n"+"运行结果为:");  
  48.         subject.request();  
  49.     }  
  50. }  

Xml代码   收藏代码
  1. 运行结果如下:此处省略了包名,***代替  
  2. true  
  3. subject的Class类是:class $Proxy0  
  4. subject中的属性有:m1, m3, m0, m2,   
  5. subject中的方法有:request, hashCode, equals, toString,   
  6. subject的父类是:class java.lang.reflect.Proxy  
  7. subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,   
  8.   
  9. 运行结果为:  
  10. before calling public abstract void ***.Subject.request()  
  11. From real subject.  
  12. after calling public abstract void ***.Subject.request()  


PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class $Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。  

        从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下: 

        从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码: 
Java代码   收藏代码
  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.     /* 
  11.      * Look up or generate the designated proxy class. 
  12.      */  
  13.     Class cl = getProxyClass(loader, interfaces);  
  14.   
  15.     /* 
  16.      * Invoke its constructor with the designated invocation handler. 
  17.      */  
  18.     try {  
  19.            /* 
  20.             * Proxy源码开始有这样的定义: 
  21.             * private final static Class[] constructorParams = { InvocationHandler.class }; 
  22.             * cons即是形参为InvocationHandler类型的构造方法 
  23.            */  
  24.         Constructor cons = cl.getConstructor(constructorParams);  
  25.         return (Object) cons.newInstance(new Object[] { h });  
  26.     } catch (NoSuchMethodException e) {  
  27.         throw new InternalError(e.toString());  
  28.     } catch (IllegalAccessException e) {  
  29.         throw new InternalError(e.toString());  
  30.     } catch (InstantiationException e) {  
  31.         throw new InternalError(e.toString());  
  32.     } catch (InvocationTargetException e) {  
  33.         throw new InternalError(e.toString());  
  34.     }  
  35. }  


        Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
        (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类. 
        (2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下: 
Java代码   收藏代码
  1. class Proxy{  
  2.     InvocationHandler h=null;  
  3.     protected Proxy(InvocationHandler h) {  
  4.         this.h = h;  
  5.     }  
  6.     ...  
  7. }  


        来看一下这个继承了Proxy的$Proxy0的源代码: 
Java代码   收藏代码
  1. public final class $Proxy0 extends Proxy implements Subject {  
  2.     private static Method m1;  
  3.     private static Method m0;  
  4.     private static Method m3;  
  5.     private static Method m2;  
  6.   
  7.     static {  
  8.         try {  
  9.             m1 = Class.forName("java.lang.Object").getMethod("equals",  
  10.                     new Class[] { Class.forName("java.lang.Object") });  
  11.   
  12.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
  13.                     new Class[0]);  
  14.   
  15.             m3 = Class.forName("***.RealSubject").getMethod("request",  
  16.                     new Class[0]);  
  17.   
  18.             m2 = Class.forName("java.lang.Object").getMethod("toString",  
  19.                     new Class[0]);  
  20.   
  21.         } catch (NoSuchMethodException nosuchmethodexception) {  
  22.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  23.         } catch (ClassNotFoundException classnotfoundexception) {  
  24.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  25.         }  
  26.     } //static  
  27.   
  28.     public $Proxy0(InvocationHandler invocationhandler) {  
  29.         super(invocationhandler);  
  30.     }  
  31.   
  32.     @Override  
  33.     public final boolean equals(Object obj) {  
  34.         try {  
  35.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
  36.         } catch (Throwable throwable) {  
  37.             throw new UndeclaredThrowableException(throwable);  
  38.         }  
  39.     }  
  40.   
  41.     @Override  
  42.     public final int hashCode() {  
  43.         try {  
  44.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  
  45.         } catch (Throwable throwable) {  
  46.             throw new UndeclaredThrowableException(throwable);  
  47.         }  
  48.     }  
  49.   
  50.     public final void request() {  
  51.         try {  
  52.             super.h.invoke(this, m3, null);  
  53.             return;  
  54.         } catch (Error e) {  
  55.         } catch (Throwable throwable) {  
  56.             throw new UndeclaredThrowableException(throwable);  
  57.         }  
  58.     }  
  59.   
  60.     @Override  
  61.     public final String toString() {  
  62.         try {  
  63.             return (String) super.h.invoke(this, m2, null);  
  64.         } catch (Throwable throwable) {  
  65.             throw new UndeclaredThrowableException(throwable);  
  66.         }  
  67.     }  
  68. }  


        接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。 

PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看一下getProxyClass的源码,很长=。= 
        2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
 

Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?  
A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。
<think>好的,用户想了解JDK动态代理的工作原理。首先,我需要回忆相关知识,并整理出清晰的步骤。根据提供的引用资料,JDK动态代理主要涉及接口、InvocationHandler和Proxy类。 首先,动态代理的核心是运行时生成代理类,而不是在编译时。这需要用到java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。用户的问题可能想知道具体是怎么生成代理类的,以及为什么必须基于接口。 接下来,我需要分步骤解释。第一步是定义接口,因为JDK动态代理只能代理接口,这是它的一个限制,可能和生成的代理类继承Proxy有关,而Java不支持多重继承,所以只能通过实现接口的方式。然后,实现InvocationHandler接口,这个接口中的invoke方法负责处理代理实例的方法调用。 然后,使用Proxy.newProxyInstance方法创建代理对象。这个方法需要类加载器、接口数组和InvocationHandler实例。生成的代理类会实现所有指定的接口,并将方法调用转发到InvocationHandler的invoke方法。 需要提到生成的代理类结构,比如每个方法调用都会转到invoke,同时可能引用资料中的例子,比如$Proxy0这样的类名。还要解释为什么必须基于接口,因为代理类已经继承了Proxy类,无法再继承其他类,所以只能通过实现接口来代理方法。 另外,性能方面,引用中提到JDK1.8后性能优于CGLIB,这可能和JVM优化有关,但用户可能不需要深入这点,除非问题涉及比较。 用户可能还会有疑问,比如动态代理的应用场景,或者和CGLIB的区别,但当前问题主要是原理。需要确保回答结构清晰,分步骤说明,并引用提供的资料来支持观点,比如引用[3]中提到的代理类结构,引用[4]中的动态生成过程。 最后,生成相关问题,可能涉及为什么需要接口、性能优化、与CGLIB对比等,参考用户提供的示例中的相关问题结构。</think>### JDK动态代理的实现原理分步解析 #### 一、核心机制 JDK动态代理基于接口代理,运行时动态生成代理类(字节码文件),核心依赖两个组件: 1. `java.lang.reflect.Proxy`:提供创建代理对象的静态方法 2. `java.lang.reflect.InvocationHandler`:代理方法调用的统一处理器[^1] #### 二、实现步骤(以转账业务为例) 1. **定义接口** ```java public interface TransferService { void transfer(String from, String to, int amount); } ``` 2. **实现目标类** ```java public class TransferServiceImpl implements TransferService { public void transfer(String from, String to, int amount) { System.out.println("执行转账:" + from + "→" + to + "金额:" + amount); } } ``` 3. **实现InvocationHandler** ```java public class AuditHandler implements InvocationHandler { private final Object target; // 被代理对象 public AuditHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[审计日志] 开始记录"); Object result = method.invoke(target, args); System.out.println("[审计日志] 记录完成"); return result; } } ``` 4. **生成代理对象** ```java TransferService proxy = (TransferService) Proxy.newProxyInstance( TransferService.class.getClassLoader(), new Class[]{TransferService.class}, new AuditHandler(new TransferServiceImpl()) ); ``` #### 三、底层实现原理 1. **字节码生成**:通过`ProxyGenerator.generateProxyClass()`方法动态生成代理类字节码 2. **类结构特征**: - 继承`Proxy`类 - 实现指定接口 - 方法调用统一转发到`InvocationHandler.invoke()` 3. **生成的代理类示例**: ```java public final class $Proxy0 extends Proxy implements TransferService { public final void transfer(String from, String to, int amount) { try { super.h.invoke(this, m3, new Object[]{from, to, amount}); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } } ``` *注:`m3`是通过反射预先获取的Method对象[^3]* #### 四、设计限制与优化 1. **必须基于接口**:Java单继承特性导致代理类必须通过实现接口扩展功能 2. **性能优势**:JDK1.8后通过优化反射调用性能(如MethodAccessor机制),效率超过CGLIB[^2] 3. **缓存机制**:相同接口组合的代理类会被复用,避免重复生成[^4] $$性能对比公式(近似): T_{proxy} = T_{invoke} + n \times T_{before} + n \times T_{after}$$ 其中$n$为增强逻辑的执行次数,$T_{invoke}$为原始方法执行时间
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值