Java动态代理

95 阅读5分钟

本篇文章主要参考了:http://blog.csdn.net/jiankunking/article/details/52143504,这篇文章写的十分详细,收获颇丰,特写这篇博客记录所得

1. 什么是动态代理?

代理不仅出现在代码中,我们的生活中也处处存在着代理的思想,例如房地产代理、各种票务的代理等。代理的思想是形容用户和服务的提供者不是直接接触、相互影响,而是通过中间代理对象去调用服务提供者的具体方法,并加入一些包装或检验行为,最终将服务结果返回给用户。而Java中的动态代理则是对静态代理类的一种升级,使我们不需要为每一个需要被代理的类编写代理类,在运行时为被代理类生成class文件,通过class的实例来实现代理。

2. JDK中的动态代理

JDK中的动态代理的使用主要分为三部分:

1)编写被代理的接口与其实现类; 2)编写InvocationHandler接口的实现; 3)调用Proxy.newProxyInstance()创建代理类的实例。

下面我们通过一个简单的例子(银行提供查询余额、存钱、取钱等操作)来说明JDK中动态代理的使用方法:

  • 编写银行bank的接口:
public interface Bank {

    public boolean drawMoney(int amount);

    public boolean saveMoney(int amount);

    public boolean checkBalance();

}
  • 编写bank的实现类:
public class BankImpl implements Bank {
    @Override
    public boolean drawMoney(int amount) {
        System.out.println("取出了"+amount+"元");
        return true;
    }

    @Override
    public boolean saveMoney(int amount) {
        System.out.println("存入了"+amount+"元");
        return true;
    }

    @Override
    public boolean checkBalance() {
        System.out.println("你的余额为:"+100+"元");
        return true;
    }
}
  • 编写InvocationHandler的实现类:
public class BankInvocationHandler implements InvocationHandler {

    private Bank bank;

    public BankInvocationHandler(Bank bank) {
        this.bank = bank;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("银行执行方法:"+method.getName()+"之前..");

        Object result = method.invoke(bank,args);
        System.out.println("执行结果为:"+result);

        System.out.println("银行执行方法:"+method.getName()+"之后..");

        return result;

    }
}
  • main函数中测试:
public static void main(String[] args) {
        Bank bank = new BankImpl();
        InvocationHandler invocationHandler = new BankInvocationHandler(bank);
        ClassLoader classLoader = bank.getClass().getClassLoader();
        Class[] interfaces = bank.getClass().getInterfaces();
        Bank bankProxy = (Bank) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

        System.out.println("动态代理类实例:"+bankProxy.getClass().getName());
        bankProxy.checkBalance();
    }
  • 测试结果:
动态代理类实例:com.sun.proxy.$Proxy0
银行执行方法:checkBalance之前..
你的余额为:100元
执行结果为:true
银行执行方法:checkBalance之后..

3.总结

上面的代码虽然简单,但是刚接触反射等概念时对BankInvocationHandler类中的invoke()方法充满疑惑,main函数中的bankProxy.checkBalance()是如何调用到BankInvocationHandler中的方法?

想要搞清楚这些就需要弄清楚动态代理是如何实现的?

首先,回到main函数中的:

Bank bankProxy = (Bank) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

这个方法用到了代理类Proxy中的newProxyInstance()方法,根据被代理的实现类BankImpl的类加载器、接口数组、自定义的代理处理器实现类创建出一个代理类实例。 但是可能更令人不解的是为什么接下来通过bankProxy.checkBalance()可以调用BankInvocationHandler中的invoke()方法,并且调用的方法checkBalance()可以作为invoke()的形参。

要想知道这其中是如何发生的,需要进入Proxy.newProxyInstance()方法中看一看:

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 信息方法,根据类加载器和接口获取代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 通过反射获取代理类的构造器并创建新实例
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            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);
        }
    }

我们发现通过方法:getProxyClass0(loader, intfs)获取到代理类,进入getProxyClass0(loader, intfs):

    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
        return proxyClassCache.get(loader, interfaces);
    }

从方法proxyClassCache.get(loader, interfaces)可以看出这个方法是在缓存中查找由loader加载并且实现了接口interfaces定义的代理类,如果没有则通过ProxyClassFactory创建代理类。

如果再往下深究,会在ProxyClassFactory类的apply()方法中找到:

//生成代理类的字节码数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  

为了弄清是如何代理的,我们可以生成一个字节码文件看一下反编译后的类是什么样的:

    private static void createProxyClassFile(){  
        String name = "ProxySubject";  
        byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});  
        FileOutputStream out =null;  
        try {  
            out = new FileOutputStream(name+".class");  
            System.out.println((new File("hello")).getAbsolutePath());  
            out.write(data);  
        } catch (FileNotFoundException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally {  
            if(null!=out) try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    } 

由上面的方法形成的代理类反编译结果如下ProxySubject:

public final class ProxySubject extends Proxy implements Bank {
    private static Method m1;
    private static Method m5;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public ProxySubject(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final boolean drawMoney(int var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m5, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean checkBalance() throws  {
        try {
            return ((Boolean)super.h.invoke(this, m3, (Object[])null)).booleanValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean saveMoney(int var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m4, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.kk.proxy.Bank").getMethod("drawMoney", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.kk.proxy.Bank").getMethod("checkBalance");
            m4 = Class.forName("com.kk.proxy.Bank").getMethod("saveMoney", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

以上的ProxySubject的代码就是:

Bank bankProxy = (Bank) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

为我们生成的动态代理类bankProxy,所以bankProxy调用checkBalance()方法其实是调用了ProxySubject类中的checkBalance():

    public final boolean checkBalance() throws  {
        try {
            //调用BankInvocationHandler的invoke()方法
            return ((Boolean)super.h.invoke(this, m3, (Object[])null)).booleanValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

动态生成的ProxySubject代理类中的属性主要就包含了几个实现的方法m1~m6,最下方的静态代码块通过反射进行初始化:

//静态代码块的执行顺序:静态代码块—>非静态代码块—>构造方法(静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new一次就执行一次。)
static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.kk.proxy.Bank").getMethod("drawMoney", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.kk.proxy.Bank").getMethod("checkBalance");
            m4 = Class.forName("com.kk.proxy.Bank").getMethod("saveMoney", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }