Java动态代理之JDK动态代理

来源:互联网 发布:关于初中语文的软件 编辑:程序博客网 时间:2024/05/18 01:43

一.
动态代理主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法。



二、demo

1.新建接口

package com.admin.user;public interface IUser {    void add();}

2.实现

package com.admin.user;public class UserImpl implements IUser {    @Override    public void add() {        System.out.println("admin");    }}

3.新建处理器

package com.admin.user;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class UserHandler implements InvocationHandler {    public Object target;    public UserHandler(Object target) {        this.target = target;    }    public Object getProxy() {        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),                this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("----- before -----");        Object result = method.invoke(target, args);        System.out.println("----- after -----");        return result;    }}

4.测试

package com.admin.user;import java.lang.reflect.InvocationHandler;public class Test {    public static void main(String[] args) {        IUser user = new UserImpl();        UserHandler handler = new UserHandler(user);        IUser proxy = (IUser)handler.getProxy();        proxy.add();    }}

5.控制台输出:
—– before —–
admin
—– after —–



三、提出问题
1.代理类的生成过程?
2.调用方法的过程?



四、源码分析(JDK1.8 )


解决问题1:
分析Test.java 就可以得出大概创建过程:

//InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,      new Class[] { Interface.class },      handler );

难点在于Proxy 的newProxyInstance()方法。先来看看JDK的注释:

/**     * Returns an instance of a proxy class for the specified interfaces     * that dispatches method invocations to the specified invocation     * handler.     *     * <p>{@code Proxy.newProxyInstance} throws     * {@code IllegalArgumentException} for the same reasons that     * {@code Proxy.getProxyClass} does.     *     * @param   loader the class loader to define the proxy class     * @param   interfaces the list of interfaces for the proxy class     *          to implement     * @param   h the invocation handler to dispatch method invocations to     * @return  a proxy instance with the specified invocation handler of a     *          proxy class that is defined by the specified class loader     *          and that implements the specified interfaces

根据JDK注释我们得知,newProxyInstance方法最终将返回一个实现了指定接口的类的实例,其三个参数分别是:ClassLoader,指定的接口及我们自己定义的InvocationHandler类。


简化版代码实现:

    public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)    {        /*         * Look up or generate the designated proxy class.         * 寻找或者生成指定的代理类         */        final Class<?>[] intfs = interfaces.clone();        Class<?> cl = getProxyClass0(loader, intfs);        /*         * Invoke its constructor with the designated invocation handler.         * 用指定的处理器 调用它的构造函数         */        try {            final Constructor<?> cons = cl.getConstructor(InvocationHandler.class);            final InvocationHandler ih = h;            return cons.newInstance(new Object[]{h});        } catch (){}    }

注意:调用newInstance的时候,传入的参数为h,即我们自己定义好的InvocationHandler类。后面我们就知道这里这样做的原因。然后可以看出newProxyInstance方法分为三部分:

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象Class clazz = Proxy.getProxyClass0(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

由此可见,动态代理真正的关键是在 getProxyClass0方法,该方法负责为一组接口动态地生成代理类类型对象。

 private static Class<?> getProxyClass0(ClassLoader loader,                                           Class<?>... interfaces) {        // 接口的数目不能超过 65535        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        //有缓存就直接返回cached copy,没有的话它将通过proxyclassfactory创建代理类。        return proxyClassCache.get(loader, interfaces);    }

proxyclassfactory的主要方法apply():

  @Override        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);            for (Class<?> intf : interfaces) {                Class<?> interfaceClass = null;                try {                    interfaceClass = Class.forName(intf.getName(), false, loader);                } catch (ClassNotFoundException e) {                }            }               ……               //代理类的字节码文件              byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName, interfaces, accessFlags);              try {                return defineClass0(loader, proxyName,                                    proxyClassFile, 0, proxyClassFile.length);            } catch (ClassFormatError e) {}                ……      }

解决问题2:
因为有了上面的生成字节码的代码,那我们可以模仿这一步,自己生成字节码文件看看,所以,我用如下代码,生成了这个最终的代理类。

public static void main(String[] args) {        IUser user = new UserImpl();        UserHandler handler = new UserHandler(user);        IUser proxy = (IUser) handler.getProxy();        proxy.add();        String path = "C:/$Proxy0.class";        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", UserImpl.class.getInterfaces());        FileOutputStream out = null;        try {            out = new FileOutputStream(path);            out.write(classFile);            out.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }

最终add()方法如下:

public final void add()    throws   {    try    {      this.h.invoke(this, m3, null);      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }

核心就在于this.h.invoke(this. m3, null);此处的h是啥呢?
我们看看这个类的类名:
public final class $Proxy0 extends Proxy implements IUser
不难发现,新生成的这个类,继承了Proxy类实现了IUser接口。所以,这里我们基本可以断定,JDK的动态代理,生成的新代理类就是继承了Proxy基类,实现了传入的接口的类。那这个h到底是啥呢?我们再看看这个新代理类,看看构造函数:

 public $Proxy0(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }

构造函数里传入了一个InvocationHandler类型的参数,看到这里,我们就应该想到之前的一行代码:
return cons.newInstance(new Object[]{h});
这是newInstance方法的最后一句,传入的h,就是这里用到的h,也就是我们最初自己定义的MyInvocationHandler类的实例。所以,我们发现,其实最后调用的add()方法,其实调用的是MyInvocationHandler的invoke()方法。我们再来看一下这个方法,找一下m3的含义,继续看代理类的源码:

tatic    {      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.admin.user.IUser").getMethod("add", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);        return;      }  

原来这个m3,就是原接口的add()方法。
最后,为什么说 jdk动态代理局限于接口?
上面已经给出了答案:
因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口。

转。
http://blog.csdn.net/zhangerqing/article/details/42504281/
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

原创粉丝点击