一篇文章彻底搞懂java动态代理的实现
2017-12-22 00:17
896 查看
网上有太多文章介绍动态代理是什么,这里就不介绍了,本文目的是让大家弄懂动态代理是如何做到这些神奇的功能的。
先来一个小demo,通过这个demo来讲解,动态代理需要三个类:
一个接口类;
一个实现接口的业务类;
一个生成动态代理类,并通过动态代理类来执行业务方法的测试类;
下面我们就一一实现它们。
接口类ITaskService
实现ITaskService 接口的业务类TaskServiceImpl
实现动态代理
三个类完成了,通过运行TestMain ,就能输出执行结果,大家先回答一个问题,以下两种情况,控制台会打印什么内容?
业务方法正常执行;
业务方法抛出异常;
如果你知道上面的答案,那么我们一起往下探索,为什么会有这样的输出结果(如果不知道,说明你还不清楚动态代理是什么,开篇说了,本文对此不做讲解,请自行度娘、谷歌,然后运行代码自己寻找答案);
假设你已经知道答案了,然后该如何入手呢?切入点就在Proxy.newProxyInstance()生成的动态代理类实例proxyInstance 上,如果我们拿到了它的源码,一切真相就浮出水面了,如何去拿呢?不卖关子,直接上代码:
这段代码就是获取动态代理生成的代理类,大家肯定看出来了,关键点就在ProxyGenerator.generateProxyClass()这个方法,为什么是这个方法?如果我们去跟踪Proxy.newProxyInstance()源码就会发现代理类的字节码最终是由这个方法构造出来的,它的执行过程非常简单,如下:
Proxy类有一个静态成员变量proxyClassCache,它就是代理类的缓存,首先根据当前Classloader和业务类的接口类调用缓存的get方法,查找缓存里是否存在代理类的Class对象(这个缓存是为了提高执行效率,避免每次调用都生成一个新的代理类),有就直接返回,如果不存在,则会调用Proxy内部类ProxyClassFactory的apply方法来构建并载入这个类,构建类就是调用了ProxyGenerator.generateProxyClass()方法来构建代理类的字节码,然后执行defineClass0()方法定义类(不清楚这个方法作用的童鞋,建议去查看下Classloader载入类的过程,当然本文不理解这个地方是没有影响,自己看着办);
我们很容就拿到了它的字节码数组,并把它生成class文件保存到了我们的硬盘上c://TaskService$proxy.class,然后通过反编译工具就获取到了它的源码:
本文重点来了,我们一步一步的解析这个类
这个代理类是final类,继承自Proxy类,并且实现了ITaskService,我提两个问题,带着问题去找答案:
1. 为什么要继承Proxy类?
2. 为什么要实现ITaskService接口?
我相信第二个问题,大家肯定都知道答案,我就不费口舌了,我们只去找第一个问题的答案,接着往下看:
四个Method成员变量,这又是什么鬼?答案在代码最后的static块中:
M1就是Object类的equals方法;
M2就是Object类的toString方法Object类的toString方法;
M3就是我们业务接口类的doTask方法;
M4就是Object类的hashCode方法;
这里又抛出一个问题,为什么要获取这四个方法呢?
M3大家肯定是没有疑问,业务方法就是我们要代理的方法;
那么equals,toString,hashCode为什么也要代理呢?其实我们想一想这三个方法的作用就知道了,如果不对这三个方法做代理,当调用这三个方法的时候,返回的就是新生成的代理对象的toString,equals,hashCode的方法,而不是我们原来业务对象的方法,执行结果肯定是不一致的,拿hashCode方法来说,动态生成的代理类与我们原来业务类是两个不同的类,所以hashCode肯定也是不一样的,这样就和我们预期结果不一样了,所以这三个方法也是需要代理的;
这里还有一个问题不知道大家有没有注意,即使我们需要代理这四个方法,为什么要专门把它们赋给成员变量呢?原因就在于执行效率,如果熟悉反射的话,就会知道getMethod方法要做各种检查、查找操作,比直接调用方法要慢很多,所以在初始化的时候获取到它们,并赋给成员变量,起到一个缓存作用;
剩下的就是业务接口实现的方法了,他们实现代码基本是一模一样,我们只拿实现的业务接口的方法来分析:
关键代码在this.h这个对象上,它又是何方神圣,如果我们去动态代理类的父类Proxy类中一找就会发现,原来它就是Proxy中的一个成员变量:
还记得我们在TestMain 方法里面创建的那个InvocationHandler实例对象吗? 这个h就是我们传入的InvocationHandler对象的实例,在接口实现方法中,就是通过它调用我们自己实现的invoke方法来实现代理逻辑。
所以流程就是,生成动态代理对象继承了Proxy类,Proxy类里面有一个成员变量持有我们自己创建的InvocationHandler对象,动态代理类在实现了业务接口的方法中,调用我们在InvocationHandler对象中实现的invoke方法来实现最终的代理逻辑;
简单吧?貌似也不是很简单,但是也没有我们想象中的那么神奇,完!
先来一个小demo,通过这个demo来讲解,动态代理需要三个类:
一个接口类;
一个实现接口的业务类;
一个生成动态代理类,并通过动态代理类来执行业务方法的测试类;
下面我们就一一实现它们。
接口类ITaskService
package com.zqz.jdkproxy; public interface ITaskService { public void doTask(); }
实现ITaskService 接口的业务类TaskServiceImpl
package com.zqz.jdkproxy; public class TaskServiceImpl implements ITaskService{ public String doTask(){ System.out.println("do task"); return "do task"; } }
实现动态代理
package com.zqz.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class TestMain { public static void main(String[] args) { /** * 通过Proxy.newProxyInstance()方法为我们的业务类生成动态代理类实例proxyInstance对象 * 它需要三个参数 * 当前的Classloader,用来加载动态生成代理类;动态代理类要实现的业务接口;InvocationHandler执行代理操作,并调用真正的业务类来执行业务方法; */ ITaskService proxyInstance = (ITaskService) Proxy.newProxyInstance( TestMain.class.getClassLoader(), //装载生成的动态代理类的classloader对象 new Class[]{ITaskService.class}, //生成的动态代理类需要实现的业务接口 new InvocationHandler(){ //InvocationHandler,调用代理操作,并执行真正的业务方法; //被代理的目标对象 ITaskService taskService = new TaskServiceImpl(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try{ //业务方法执行前打印 System.out.println("before"); //调用业务方法 Object obj = method.invoke(taskService, args); //业务方法执行后打印 System.out.println("after"); return obj; } catch (Throwable e){ //业务方法抛出异常打印 System.out.println("exception"); throw e; } finally{ //业务方法执行完成打印(无论是否发生异常) System.out.println("finally"); } } } ); //使用代理对象执行代理方法 proxyInstance.doTask(); } }
三个类完成了,通过运行TestMain ,就能输出执行结果,大家先回答一个问题,以下两种情况,控制台会打印什么内容?
业务方法正常执行;
业务方法抛出异常;
如果你知道上面的答案,那么我们一起往下探索,为什么会有这样的输出结果(如果不知道,说明你还不清楚动态代理是什么,开篇说了,本文对此不做讲解,请自行度娘、谷歌,然后运行代码自己寻找答案);
假设你已经知道答案了,然后该如何入手呢?切入点就在Proxy.newProxyInstance()生成的动态代理类实例proxyInstance 上,如果我们拿到了它的源码,一切真相就浮出水面了,如何去拿呢?不卖关子,直接上代码:
byte[] bytes = ProxyGenerator.generateProxyClass("TaskService$proxy", new Class[]{ITaskService.class}); FileOutputStream fos = new FileOutputStream(new File("c://TaskService$proxy.class")); fos.write(bytes); fos.flush(); fos.close(); Object obj = new Object();
这段代码就是获取动态代理生成的代理类,大家肯定看出来了,关键点就在ProxyGenerator.generateProxyClass()这个方法,为什么是这个方法?如果我们去跟踪Proxy.newProxyInstance()源码就会发现代理类的字节码最终是由这个方法构造出来的,它的执行过程非常简单,如下:
Proxy类有一个静态成员变量proxyClassCache,它就是代理类的缓存,首先根据当前Classloader和业务类的接口类调用缓存的get方法,查找缓存里是否存在代理类的Class对象(这个缓存是为了提高执行效率,避免每次调用都生成一个新的代理类),有就直接返回,如果不存在,则会调用Proxy内部类ProxyClassFactory的apply方法来构建并载入这个类,构建类就是调用了ProxyGenerator.generateProxyClass()方法来构建代理类的字节码,然后执行defineClass0()方法定义类(不清楚这个方法作用的童鞋,建议去查看下Classloader载入类的过程,当然本文不理解这个地方是没有影响,自己看着办);
我们很容就拿到了它的字节码数组,并把它生成class文件保存到了我们的硬盘上c://TaskService$proxy.class,然后通过反编译工具就获取到了它的源码:
import com.zqz.jdkproxy.ITaskService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class TaskService$proxy extends Proxy implements ITaskService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public TaskService$proxy(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { 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 String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final void doTask() throws { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { } throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }
本文重点来了,我们一步一步的解析这个类
public final class TaskService$proxy extends Proxy implements ITaskService
这个代理类是final类,继承自Proxy类,并且实现了ITaskService,我提两个问题,带着问题去找答案:
1. 为什么要继承Proxy类?
2. 为什么要实现ITaskService接口?
我相信第二个问题,大家肯定都知道答案,我就不费口舌了,我们只去找第一个问题的答案,接着往下看:
private static Method m1; private static Method m2; private static Method m3; private static Method m0;
四个Method成员变量,这又是什么鬼?答案在代码最后的static块中:
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { } throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); }
M1就是Object类的equals方法;
M2就是Object类的toString方法Object类的toString方法;
M3就是我们业务接口类的doTask方法;
M4就是Object类的hashCode方法;
这里又抛出一个问题,为什么要获取这四个方法呢?
M3大家肯定是没有疑问,业务方法就是我们要代理的方法;
那么equals,toString,hashCode为什么也要代理呢?其实我们想一想这三个方法的作用就知道了,如果不对这三个方法做代理,当调用这三个方法的时候,返回的就是新生成的代理对象的toString,equals,hashCode的方法,而不是我们原来业务对象的方法,执行结果肯定是不一致的,拿hashCode方法来说,动态生成的代理类与我们原来业务类是两个不同的类,所以hashCode肯定也是不一样的,这样就和我们预期结果不一样了,所以这三个方法也是需要代理的;
这里还有一个问题不知道大家有没有注意,即使我们需要代理这四个方法,为什么要专门把它们赋给成员变量呢?原因就在于执行效率,如果熟悉反射的话,就会知道getMethod方法要做各种检查、查找操作,比直接调用方法要慢很多,所以在初始化的时候获取到它们,并赋给成员变量,起到一个缓存作用;
剩下的就是业务接口实现的方法了,他们实现代码基本是一模一样,我们只拿实现的业务接口的方法来分析:
public final void doTask() throws { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); }
关键代码在this.h这个对象上,它又是何方神圣,如果我们去动态代理类的父类Proxy类中一找就会发现,原来它就是Proxy中的一个成员变量:
protected InvocationHandler h;
还记得我们在TestMain 方法里面创建的那个InvocationHandler实例对象吗? 这个h就是我们传入的InvocationHandler对象的实例,在接口实现方法中,就是通过它调用我们自己实现的invoke方法来实现代理逻辑。
所以流程就是,生成动态代理对象继承了Proxy类,Proxy类里面有一个成员变量持有我们自己创建的InvocationHandler对象,动态代理类在实现了业务接口的方法中,调用我们在InvocationHandler对象中实现的invoke方法来实现最终的代理逻辑;
简单吧?貌似也不是很简单,但是也没有我们想象中的那么神奇,完!
相关文章推荐
- 转:Java 动态代理的内部实现机制(大体意思正确,写的还行的一篇文章)
- 共享一篇红薯大哥在2002年在IBM上的“使用 JAVA 中的动态代理实现数据库连接池”
- AOP初学者第一步:用Java动态代理实现AOP
- 使用JAVA中的动态代理实现数据库连接池
- 使用JAVA中的动态代理实现数据库连接池
- Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP
- 使用JAVA中的动态代理实现数据库连接池
- 使用Java动态代理实现AOP
- 设计模式:用Java动态代理实现AOP
- 使用Java动态代理实现AOP
- 使用JAVA中的动态代理实现数据库连接池
- 使用JAVA中的动态代理实现数据库连接池
- 用Java动态代理实现AOP
- 使用JAVA中的动态代理实现数据库连接池 Z
- 用Java动态代理实现AOP
- 迈出AOP第一步--使用Java 动态代理实现AOP[zz]
- Java动态代理实现
- 使用JAVA中的动态代理实现数据库连接池
- Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP(转)
- Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP