代码详解动态代理
2016-04-23 08:19
537 查看
参考
《深入理解JAVA虚拟机》-周志明
《张孝祥系列》
场景
java的动态代理到底是怎么回事?
怎么为已经存在的多个具有‘相同接口’的‘目标类’的各个方法增加一些系统功能,例如,异常处理、日志、事物与缓存等?
目标类:系统各个业务DAO,eg、UserDaoImpl;相同接口:IBaseDAO => 怎么为 业务DAO增加缓存功能 ?
分析
以上问题的解决,需要用到一个代理类,代理类在目标类各方法的执行周围或者说前后,增加系统交叉业务功能。如果为每一个目标类写一个代理类那就太麻烦了!
对于这类普遍使用的有意义的共性问题,JDK总是为我们提供了一套解决方案-在这个问题上,就提供了一个
java.lang.reflect.Proxy类。
这个类的字节码,不是像一般情况那样:编译器编译.java文件,生成 .class文件,然后再由类加载器加载到内存中成为运行时字节码。而是JVM直接在运行时动态生成的!
实验
总结
关键认识到:JVM有能动态生成字节码这种机制
《深入理解JAVA虚拟机》-周志明
《张孝祥系列》
场景
java的动态代理到底是怎么回事?
怎么为已经存在的多个具有‘相同接口’的‘目标类’的各个方法增加一些系统功能,例如,异常处理、日志、事物与缓存等?
目标类:系统各个业务DAO,eg、UserDaoImpl;相同接口:IBaseDAO => 怎么为 业务DAO增加缓存功能 ?
分析
以上问题的解决,需要用到一个代理类,代理类在目标类各方法的执行周围或者说前后,增加系统交叉业务功能。如果为每一个目标类写一个代理类那就太麻烦了!
对于这类普遍使用的有意义的共性问题,JDK总是为我们提供了一套解决方案-在这个问题上,就提供了一个
java.lang.reflect.Proxy类。
这个类的字节码,不是像一般情况那样:编译器编译.java文件,生成 .class文件,然后再由类加载器加载到内存中成为运行时字节码。而是JVM直接在运行时动态生成的!
实验
package cool.pengych.java.proxy; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; /** * @author pengyucheng * date 2016/04/22 * * 静态代理技术:为系统中的各种接口的类增加代理功能(下文以下日志为例),如果手动实现,会疯掉? 因为系统中几乎所有的类都需要写日志。 * 动态代理技术应运而生! * JVM可以在运行期动态生成类的字节码,这种动态生成的类往往被用作代理类-动态代理 * 限制:JVM生成的动态类必须实现一个或者多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。 * */ public class ProxyTest { public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { /* * 认识JVM动态生成的代理类 * 1、代理类名称 * 2、类的构造方法及参数 */ Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy1.getName());// 代理类名 : com.sun.proxy.$Proxy0 Constructor[] constructors = clazzProxy1.getConstructors(); for (Constructor constructor : constructors) { StringBuilder sb = new StringBuilder(constructor.getName()); sb.append("("); Class[] clazzType = constructor.getParameterTypes(); for (Class clazzParam : clazzType) { sb.append(clazzParam.getName()); sb.append(","); } if(clazzType != null ) { sb.deleteCharAt(sb.lastIndexOf(",")); } sb.append(")"); System.out.println(sb.toString()); // 代理类的构造方法 : com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler) } Method[] methods = clazzProxy1.getMethods(); for (Method method : methods) { StringBuilder sb = new StringBuilder(method.getName()); sb.append("("); Class[] clazzType = method.getParameterTypes(); for (Class clazzParam : clazzType) { sb.append(clazzParam.getName()); sb.append(","); } if(clazzType != null && clazzType.length>0) { sb.deleteCharAt(sb.lastIndexOf(",")); } sb.append(")"); System.out.println(sb.toString()); // 代理类的普通方法 } /* * 创建动态类的实例对象的两种方式 * 1、分步进行:先创建的动态类的字节码,再通过构造器,创建实例 * 2、一步到位。 */ //分步进行 Class clazzProxy2 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); Constructor constructor = clazzProxy2.getConstructor(InvocationHandler.class); Collection proxyCollection = (Collection) constructor.newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); System.out.println(proxyCollection); // 返回 null 对象 // proxyCollection.size(); proxyCollection.clear(); // 一步到位 Collection proxy2 = (Collection) Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[] {Collection.class}, new InvocationHandler() { ArrayList target = new ArrayList<>(); // 目标对象 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long beginTime = System.currentTimeMillis(); // 系统交叉业务 Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); return retVal; } }); proxy2.add("pingpang"); proxy2.add("basketball"); System.out.println(proxy2); /* * 终极版 动态代理之 AOP模型 * 客户端只需要提供 目标类 与 系统交叉业务,即可获取动态代理类。 */ final ArrayList target = new ArrayList(); // 目标对象 Advice advice = new Myadvice(); // ArrayList proxy = (ArrayList) getProxy(target,advice);// com.sun.proxy.$Proxy1 cannot be cast to java.util.ArrayList Collection proxy = (Collection) getProxy(target,advice); //代理对象必须与目标类有相同的接口 System.out.println(proxy); } /** * @param target 目标对象 * @param advice 系统业务 * @return 代理对象 */ private static Object getProxy(final Object target,final Advice advice) { Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(),/*Collection.class.getClassLoader(),*/ target.getClass().getInterfaces(),/*new Class[] {Collection.class},*/ new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } }); return proxy3; } }
总结
关键认识到:JVM有能动态生成字节码这种机制
相关文章推荐
- java多线程
- Java虚拟机学习总结目录
- C#_会员管理系统:开发三(修改密码)
- Java程序通用场景性能分析
- JAVA 公共方法 之 date时间处理
- java.util.Timer分析源码了解原理
- C++ 线程操作
- 用JAVA捕获屏幕、屏幕录像、播放
- mongo
- OpenStack远程调试 - eclipse + pydev (二)
- 使用C/C++扩展Python
- Asp.net 面向接口可扩展框架之核心容器(含测试代码下载)
- jdbc学习
- 《用 Python 学微积分》笔记 2
- java事务(三)——自己实现分布式事务
- 设计模式 - 观察者模式(Observe pattern)C++实现
- PHP7之常量数组
- VB拖放(随记,未完)
- Atitti. 语法树AST、后缀表达式、DAG、三地址代码
- Atitti. 语法树AST、后缀表达式、DAG、三地址代码