Java那些事:动态代理

来源:互联网 发布:后端数据库的设计优化 编辑:程序博客网 时间:2024/05/20 19:28

       动态代理是对代理模式的一种实现,JDK支持动态代理。静态代理是由编写人员自己编写编译,动态代理的代理类则是由代码帮助生成。本文将探索如何使用动态代理以及其原理,做到知其然知其所以然。

      

         Java动态代理相关的类和接口:

         java.lang.reflect中的InterfaceInvocationHandler,

         其中有且仅有有 Objectinvoke(Object proxy,Method method,Object[] args) throws Throwable这一个方法

 

         Proxy类

         其中提供了四个静态方法

                   getInvocationHandler(Objectproxy) 获取代理对象的handler

                   getProxyClass(ClassLoaderloader,Class<?>… interfaces)  获取代理类,需要传入加载代理类的类加载器和需要代理类实现的一组接口

                   isProxyClass(Class<?>cl)  检查某个类是否是代理类

                   newProxyInstance(ClassLoaderloader,Class<?>[] interfaces,InvocationHandler h) 被委托类(真实类)的类加载器,需要代理类实现的一组接口,关联的handler,这个方法是对其他方法的封装,返回一个代理类的实例。

         每个代理类实例都有一个实现了invocationhandler的对象与之关联,代理实例会调用关联的Handler的invoke方法,最大问题出现---代理类在哪里?

在代码中,使用JDK提供的动态代理还是相当的简便,代码如下:

        

         公共接口:

public interface ShouldDoSomeThing {/** * 实现此接口的类,需要实现这个方法 */public void doSomeThing();}

      Handler类:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class HandlerClass implements InvocationHandler{private Object o;public HandlerClass(Object o){this.o = o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("do before!");method.invoke(o, args);System.out.println("do after!");return null;}}

需要特别提醒:这个handler类在上面已经有叙述,其并不是代理类。

 

     然后是客户端类:

public class Main {public static void main(String[] args) {RealClass rc = new RealClass();HandlerClass pc = new HandlerClass(rc);//这里必须转型到某个接口ShouldDoSomeThing ProxyTest = (ShouldDoSomeThing) Proxy.newProxyInstance(rc.getClass().getClassLoader(), rc.getClass().getInterfaces(), pc);ProxyTest.doSomeThing();}}

输出:do before!do some thing!do after!

     从这里可以看出,代理类实例可以安全的转型到被委托类实现了接口的类型。那代理类到底在哪里?输出为什么会是这样?

首先根据客户端的使用流程说下整个过程:

         被委托类对象被创建,这个类是实际操作的执行者。然后Handler被创建,并且传入了被委托类对象,接下来,proxy对象被创建。

         调用doSomeThing方法,此时,代理类对象所实现的相应的接口方法被调用,在代理类方法的内部,调用了与之相关联的handler的invoke方法,而invoke方法中调用了被委托类的相应接口的方法,所以实际的操作被执行,相应在invoke方法中附加的相关操作也被执行。

上面的描述,可以使用一个UML图来表示:





        图中展示了整个动态代理的过程,可以看出,代理类是由Proxy来创建,而且客户端是和代理类交流,可见,JDK的动态代理是严格遵守代理模式。其中的$ProxyN虽然是个类,但是其在外存上并不会存在.class文件,其在运行过程中动态生成,存在于内存中的某个角落。但是要理解RealClass是如何和Handler关联,以及代理类是如何调用invoke方法,还必须深入到源代码。


首先看看创建Proxy对象的源代码

 /**     *      * @param loader 类加载器     * @param interfaces 需要代理类所实现的一组接口     * @param h 与代理类关联的handler(需要执行invoke方法的handler对象)     * @return 代理类对象     * @throws IllegalArgumentException     */            @CallerSensitive    public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException    {        Objects.requireNonNull(h);                // 安全检查        final SecurityManager sm = System.getSecurityManager();        if (sm != null) {            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);        }                /*         * 查看是否已经存在代理类,如果存在,就返回它,如果不存在,就创建         */                Class<?> cl = getProxyClass0(loader, interfaces);        /*         * Invoke its constructor with the designated invocation handler.         */        try {                    if (sm != null) {                checkNewProxyPermission(Reflection.getCallerClass(), cl);            }                        //这是关键代码,首先获得了其构造器,带有h参数的那个构造器            final Constructor<?> cons = cl.getConstructor(constructorParams);            final InvocationHandler ih = h;            //设置权限            if (!Modifier.isPublic(cl.getModifiers())) {                AccessController.doPrivileged(new PrivilegedAction<Void>() {                    public Void run() {                        cons.setAccessible(true);                        return null;                    }                });            }                        //返回这个代理对象            return cons.newInstance(new Object[]{h});                                                        } catch (IllegalAccessException|InstantiationException e) {            throw new InternalError(e.toString(), e);        } catch (InvocationTargetException e) {            Throwable t = e.getCause();            if (t instanceof RuntimeException) {                throw (RuntimeException) t;            } else {                throw new InternalError(t.toString(), t);            }        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString(), e);        }    }

    接下来去看看如何创建的代理类$ProxyN:

首先是getProxyClass0


/*     *      * @param loader 类加载器     * @param interfaces 需要实现的一组接口     * @return 代理类的Class对象     */    private static Class<?> getProxyClass0(ClassLoader loader,                                           Class<?>... interfaces) {        if (interfaces.length > 65535) {            throw new IllegalArgumentException("interface limit exceeded");        }        // If the proxy class defined by the given loader implementing        // the given interfaces exists, this will simply return the cached copy;        // otherwise, it will create the proxy class via the ProxyClassFactory                //如果在map中存在,就返回,不存在创建        return proxyClassCache.get(loader, interfaces);    }

      忽略相关细节,直接来看如何创建代理类的:


public static Class<?> getProxyClass(ClassLoader loader,                                            Class<?>... interfaces)      throws IllegalArgumentException      {      // 如果目标类实现的接口数大于65535个则抛出异常,真的会存在这么多的接口吗?    if (interfaces.length > 65535) {          throw new IllegalArgumentException("interface limit exceeded");      }        // 声明代表proxy类的class对象      Class proxyClass = null;        String[] interfaceNames = new String[interfaces.length];        Set interfaceSet = new HashSet();   // for detecting duplicates        // 遍历目标类所实现的接口,并将其写入到内存中    for (int i = 0; i < interfaces.length; i++) {                    // 获得需要代理类所实现的接口的名称          String interfaceName = interfaces[i].getName();          Class interfaceClass = null;          try {          // 加载需要代理类实现的接口到内存中          interfaceClass = Class.forName(interfaceName, false, loader);          } catch (ClassNotFoundException e) {          }          if (interfaceClass != interfaces[i]) {          throw new IllegalArgumentException(              interfaces[i] + " is not visible from class loader");          }            //省略一些无关紧要的代码 .......                    // 把目标类实现的接口代表的Class对象放到Set中          interfaceSet.add(interfaceClass);            interfaceNames[i] = interfaceName;      }        // 把目标类实现的接口名称作为缓存(Map)中的key      Object key = Arrays.asList(interfaceNames);        Map cache;            synchronized (loaderToCache) {          // 从缓存中获取cache          cache = (Map) loaderToCache.get(loader);          if (cache == null) {          // 如果获取不到,则new个HashMap实例          cache = new HashMap();          // 把HashMap实例和当前加载器放到缓存中          loaderToCache.put(loader, cache);          }        }        synchronized (cache) {            do {          // 根据接口的名称从缓存中获取对象          Object value = cache.get(key);          if (value instanceof Reference) {              proxyClass = (Class) ((Reference) value).get();          }          if (proxyClass != null) {              // 如果代理对象的Class实例已经存在,则直接返回              return proxyClass;          } else if (value == pendingGenerationMarker) {              try {              cache.wait();              } catch (InterruptedException e) {              }              continue;          } else {              cache.put(key, pendingGenerationMarker);              break;          }          } while (true);      }        try {          // 中间省略部分代码 .......                    // 这里就是动态生成代理对象的最关键的地方          byte[] proxyClassFile = ProxyGenerator.generateProxyClass(              proxyName, interfaces);          try {              // 根据代理类的字节码生成代理类的实例              proxyClass = defineClass0(loader, proxyName,              proxyClassFile, 0, proxyClassFile.length);          } catch (ClassFormatError e) {              throw new IllegalArgumentException(e.toString());          }          }          // add to set of all generated proxy classes, for isProxyClass          proxyClasses.put(proxyClass, null);        }       // 中间省略了一些代码 .......            return proxyClass;      }  

       从中可以看出,它遍历了传入的所有接口并获取了它们的名称,接下来就是利用字节码操作生成相关的class字节码,然后看看获取字节码的generateProxyClass类:


public static byte[] generateProxyClass(final String name,                                             Class[] interfaces)     {         ProxyGenerator gen = new ProxyGenerator(name, interfaces);      // 这里动态生成代理类的字节码,涉及字节码操作,目前还看不了         final byte[] classFile = gen.generateClassFile();        // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上         if (saveGeneratedFiles) {             java.security.AccessController.doPrivileged(             new java.security.PrivilegedAction<Void>() {                 public Void run() {                     try {                         FileOutputStream file =                             new FileOutputStream(dotToSlash(name) + ".class");                         file.write(classFile);                         file.close();                         return null;                     } catch (IOException e) {                         throw new InternalError(                             "I/O exception saving generated file: " + e);                     }                 }             });         }        // 返回代理类的字节码         return classFile;     }  

     上面的两个类可以说明JDK是如何生成的代理类的字节码,现在需要明白的就是,它们之间的关系是如何建立的。这个就需要查看代理类的源代码,直接利用创建字节码类来生成其.class文件,然后反编译得到源代码。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true)

源代码如下:


public final class $Proxy11 extends Proxy implements ShouldDoSomeThing {// 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例public $Proxy11(InvocationHandler invocationhandler) {super(invocationhandler);}public final boolean equals(Object obj) {try {return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })).booleanValue();} catch (Error _ex) {} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}/* * 实现了相关的接口 */@Overridepublic void doIt(String str) {try {// 从这里可以看出,在代理类内部所实现的需要实现的接口是调用了// 与之相关联的handler对象的invoke方法,那handler对象// 哪里来的?还记得在newInstance的时候传入的pc吗?它就来// 自那里!super.h.invoke(this, m3, null);return;} catch (Error _ex) {} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return ((Integer) super.h.invoke(this, m0, null)).intValue();} catch (Error _ex) {} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String) super.h.invoke(this, m2, null);} catch (Error _ex) {} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}private static Method m1;private static Method m3;private static Method m0;private static Method m2;// 这里利用反射,获得了代理类其中关键的是m3这个方法,而且m3这个方法还是委// 托类的,同时这个m3作为参数,传递给了handler的invoke方法static {try {m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });m3 = Class.forName("dp.ShouldDoSomeThing").getMethod("ShouldDoSomeThing", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]);} catch (NoSuchMethodException nosuchmethodexception) {throw new NoSuchMethodError(nosuchmethodexception.getMessage());} catch (ClassNotFoundException classnotfoundexception) {throw new NoClassDefFoundError(classnotfoundexception.getMessage());}}}

        到目前为止,整个原理都已经解释,其中一些和思想无关的代码并没有描述。JDK的动态代理使用了字节码操作生成代理类的字节码,使用反射来执行代理,现在再看上面的图如果能够感觉到思路清晰,那么动态代理的思想也就基本OK了,但是JDK动态代理只能是代理接口,并不能代理类,CGLIB 是弥补了这一缺点。

         由于最近需要搭建一个基础的Web框架,所以就看了下动态代理在AOP方面的运用,当然,代理不仅仅是AOP方面才有运用,代理是对委托对象访问的一种控制,在许多方面都有用,“设计”这种东西有时候真心很费脑细胞,实现反而更加简单。

         JDK1.8对Proxy的改进相当大,由于我windows的JDK是1.8,那个真心复杂了很多!不利于对动态代理整体的掌握,所以本文参考了http://rejoy.iteye.com/blog/1627405#comments的部分代码并有部分改变。

         再说下那个代理类名字的问题:$Proxy是所有代理类的名字前缀,其后N代表一个数字,每次在map中新增一个class对象的时候都会使数字自增1,,所以代理类的名字可能是这样的----$Proxy20

         薄言有误,还请指出一起学习改进。



1 0