java动态代理

来源:互联网 发布:开淘宝店难吗 编辑:程序博客网 时间:2024/06/05 17:19

动态代理

熟悉设计模式的人对于代理模式可 能都不陌生。 代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互。代理的存在对于调用者来说是透明的,调用者看到的只是接口。代理对象则可以封装一些内部的处理逻辑,如访问控制、远程通信、日志、缓存等。比如一个对象访问代理就可以在普通的访问机制之上添加缓存的支持。这种模式在RMI和EJB中都得到了广泛的使用。传统的代理模式的实现,需要在源代码中添加一些附加的类。这些类一般是手写或是通过工具来自动生成。

JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandlerinvoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。

下面用一个买东西的例子说明动态代理

抽象角色:Buyer接口

package test.dynamicproxy;public interface Buyer {public void buy(String name,int price);}

真正角色:RealBuyer

package test.dynamicproxy;public class RealBuyer implements Buyer {@Overridepublic void buy(String name, int price) {System.out.println("商品 :"+name+" , 价格:"+price);}}

代理类需要实现InvocationHandler接口,实现invoke方法

package test.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class DynamicProxyBuyer implements InvocationHandler {private Object target;public DynamicProxyBuyer(Object object) {this.target = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {before();Object object = method.invoke(target, args);after();return object;}public static void before() {System.out.println("------before invoke()---------比价过程-----------");}public static void after() {System.out.println("------after invoke()---------支付过程-----------");}}

测试类:

package test.dynamicproxy;import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) {Buyer realBuyer = new RealBuyer();DynamicProxyBuyer dpb = new DynamicProxyBuyer(realBuyer);Buyer buyer = (Buyer)Proxy.newProxyInstance(Test.class.getClassLoader(), realBuyer.getClass().getInterfaces(),dpb);buyer.buy("TV", 3000);}}

这样一个动态代理的例子就完成了,程序的最终输出:

------before invoke()---------比价过程-----------商品 :TV , 价格:3000------after invoke()---------支付过程-----------

可以看出跟传统的代理模式相比,在动态代理中代理类没有实现和真正角色相同的抽象接口,而是实现了一个InvocationHandler接口。InvocationHandler接口非常的简单,只有一个invoke()方法

在测试类中,我们调用了Proxy类的一个静态方法newProxyInstance(),把一个classloader、被代理类实现的一系列接口以及InvocationHandler的实例作为参数传了进去,这个静态方法就将动态生成一个代理类,这个动态代理类含有一个以invocationHandler类型为参数的唯一参数类型构造方法,然后通过反射机制获取这个动态代理类的构造方法,将上述第三个参数InvocationHandler的实例注入进去,创建一个动态代理类的实例,返回这个实例。

具体有如下四步骤:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

Proxy类的newProxyInstance方法的源码中验证上述步骤

public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException    {        if (h == null) {            throw new NullPointerException();        }        /*         * Look up or generate the designated proxy class.         */        Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor 获取动态代理类的Class对象(由于缓存的作用,所以是获取或者创建)        /*         * Invoke its constructor with the designated invocation handler.         */        try {            final Constructor<?> cons = cl.getConstructor(constructorParams);  //通过反射获取构造器            final InvocationHandler ih = h;            SecurityManager sm = System.getSecurityManager();  //一些安全检查            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {                // create proxy instance with doPrivilege as the proxy class may                // implement non-public interfaces that requires a special permission                return AccessController.doPrivileged(new PrivilegedAction<Object>() {                    public Object run() {                        return newInstance(cons, ih); //获取动态代理类的一个实例                    }                });            } else {                return newInstance(cons, ih);            }        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString());        }    }

上述代码的关键一步就是Class<?> cl = getProxyClass0(loader, interfaces); 也就是创建或者获取动态代理类的Class对象,我们跟进getProxyClass0(),这个方法的作用相当于反射中Class.forName( )方法,只不过这其中夹杂着一些检查和定制内容。

看一下getProxyClass0( )的前半部分

private static Class<?> getProxyClass0(ClassLoader loader,                                           Class<?>... interfaces) {        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller            final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME);            final ClassLoader ccl = caller.getClassLoader();            checkProxyLoader(ccl, loader);            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);        }        if (interfaces.length > 65535) {            throw new IllegalArgumentException("interface limit exceeded");        }        Class<?> proxyClass = null;        /* collect interface names to use as key for proxy class cache */        String[] interfaceNames = new String[interfaces.length];        // for detecting duplicates        Set<Class<?>> interfaceSet = new HashSet<>();        for (int i = 0; i < interfaces.length; i++) {            /*             * Verify that the class loader resolves the name of this             * interface to the same Class object.             */            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");            }            /*             * Verify that the Class object actually represents an             * interface.             */            if (!interfaceClass.isInterface()) {                throw new IllegalArgumentException(                    interfaceClass.getName() + " is not an interface");            }            /*             * Verify that this interface is not a duplicate.             */            if (interfaceSet.contains(interfaceClass)) {                throw new IllegalArgumentException(                    "repeated interface: " + interfaceClass.getName());            }            interfaceSet.add(interfaceClass);            interfaceNames[i] = interfaceName;        }

做了一些安全检查工作,包括检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的。接口的数目不能超过 65535,这是 JVM 设定的限制。还会检查确保是 interface 类型而不是 class 类型。这个步骤通过一个循环来完成,检查通过后将会得到一个包含所有接口名称的字符串数组,记为 String[] interfaceNames。

然后介绍一下,Proxy类设置了一个私有的静态变量容器,用作缓存每个ClassLoader加载的动态代理类

/** maps a class loader to the proxy class cache for that loader */    private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache        = new WeakHashMap<>();

之所以用classloader作为这么Map的关键字key,是因为即使是同样的字节码,不一样的ClassLoader加载形成的对象也是不一样的,这个缓存的value还是一个Map,这个Map的key就是所有接口名称构成的字符串,Key的value就是实现这一系列接口的动态代理类的Class对象。

do {     // 以接口名字列表作为关键字获得对应 cache 值    Object value = cache.get(key);     if (value instanceof Reference) {         proxyClass = (Class) ((Reference) value).get();     }     if (proxyClass != null) {         // 如果已经创建,直接返回        return proxyClass;     } else if (value == pendingGenerationMarker) {         // 代理类正在被创建,保持等待        try {             cache.wait();         } catch (InterruptedException e) {         }         // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待        continue;     } else {         // 标记代理类正在被创建        cache.put(key, pendingGenerationMarker);         // break 跳出循环已进入创建过程        break; } while (true);

动态创建代理类的类对象。首先是确定代理类所在的包,其原则如前所述,如果都为 public 接口,则包名为空字符串表示顶层包;如果所有非 public 接口都在同一个包,则包名与这些接口的包名相同;如果有多个非 public 接口且不同包,则抛异常终止代理类的生成。确定了包后,就开始生成代理类的类名,同样如前所述按格式“$ProxyN”生成。类名也确定了,接下来就是见证奇迹的发生 —— 动态生成代理类:
// 动态地生成代理类的字节码数组byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try {     // 动态地定义新生成的代理类    proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0,         proxyClassFile.length); } catch (ClassFormatError e) {     throw new IllegalArgumentException(e.toString()); } // 把生成的代理类的类对象记录进 proxyClasses 表proxyClasses.put(proxyClass, null);

由此可见,所有的代码生成的工作都由神秘的 ProxyGenerator 所完成了,当你尝试去探索这个类时,你所能获得的信息仅仅是它位于并未公开的 sun.misc 包,有若干常量、变量和方法以完成这个神奇的代码生成的过程,但是 sun 并没有提供源代码以供研读。至于动态类的定义,则由 Proxy 的 native 静态方法 defineClass0 执行。


参考:IBM DeveloperWork 关于动态代理机制的分析

IBM DeveloperWork 关于动态代理机制的分析2





原创粉丝点击