代码详解动态代理

来源:互联网 发布:嵌入式底层软件开发 编辑:程序博客网 时间:2024/05/21 10:58

参考

《深入理解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.$Proxy0Constructor[] 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(){@Overridepublic 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<>(); // 目标对象@Overridepublic 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.ArrayListCollection 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() {@Overridepublic 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有能动态生成字节码这种机制


0 0