Java动态代理深度解析

来源:互联网 发布:知乎回答排名规则 编辑:程序博客网 时间:2024/06/10 23:39

Java动态代理深度解析

引言

说起动态代理,很多人可能都没有直接去使用过。但是只要用过Spring,那动态代理就是一个是个绕不过的坎,因为Spring的核心特性之一AOP就是基于动态代理来实现的,那么什么情况下需要用到动态代理呢?

场景

考虑这样一个教师的接口:

public interface Teacher {    void teach();}

假设我们有一个TeacherChan的实现类,陈老师教的是摄影:

public class TeacherChan implements Teacher {    @Override    public void teach() {        System.out.println("大家好,我是陈老师,我教大家摄影!");    }}

另外还有一个TeacherCang的实现类,苍老师教的是生物:

public class TeacherCang implements Teacher {    @Override    public void teach() {        System.out.println("大家好,我是苍老师,我教大家生物!");    }}

不管是陈老师还是苍老师,只要实现了Teacher这个接口,给我们传道授业解惑,为了礼貌起见,我们总应该给人家问声好吧。而问好这件事不需要老师主动要求,可以交给代理来做,每次有老师来上课,代理自动做了问好这件事。而代理类又分为静态代理和动态代理,静态代理在编写代码时已经确定了要代理的类,只能代理单一的类型,在此略过,今天重点讲动态代理。

Java动态代理

Java动态代理创建代理类的方法为:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

其中ClassLoader是用来定义代理类的class文件的,用系统默认的就好,interfaces是要代理的接口,InvocationHandler是用来实际执行代理方法的接口,常用做法是实现该接口,并将需要代理的类实例对象传进去。
实现自己的方法执行器:

public class JdkDynamicProxy implements InvocationHandler {    private Object proxied;    public JdkDynamicProxy(Object object) {        this.proxied = object;    }    /**     * proxy为创建的代理类实例,method是本次被代理的方法,args是方法的参数     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("-------------------老师好[by jdk动态代理]-------------------");        // 执行代理方法        Object obj = method.invoke(proxied, args);        System.out.println("-------------------老师再见[by jdk动态代理]-------------------");        // 返回方法执行结果        return obj;    }}

创建代理类对象并执行:

// 代理陈老师Teacher proxy1 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),        new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherChan()));proxy1.teach();// 代理苍老师Teacher proxy2 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),        new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherCang()));proxy2.teach();

输出结果:

-------------------老师好[by jdk动态代理]-------------------大家好,我是陈老师,我教大家摄影!-------------------老师再见[by jdk动态代理]--------------------------------------老师好[by jdk动态代理]-------------------大家好,我是苍老师,我教大家生物!-------------------老师再见[by jdk动态代理]-------------------

实际上,Java会过滤掉接口所有final、native等方法,并为剩下的所有符合条件的方法生成代理方法。而且,熟悉Spring的朋友应该知道,Spring的AOP机制的实现不仅使用了Java的动态代理,而且还引入了CGLib。因为Java的动态代理只能代理接口,而不能代理原始的类。那么为什么Java不能代理类呢,答案是Java的单继承机制。

深入Java动态代理的实现

Java的动态代理是怎么实现的呢?其实很简单,就是运行时生成一个代理类,该类实现了需要代理的接口,并返回这个代理类的实例对象给调用者。调试进入Proxy.newProxyInstance()的方法内部,可以看到在Proxy内部生成class字节码的方法:

// 生成的代理类名前缀private static final String proxyClassNamePrefix = "$Proxy";// 生成的代理类名序号private static final AtomicLong nextUniqueNumber = new AtomicLong();// 序号值加1long num = nextUniqueNumber.getAndIncrement();// 代理类名:$ProxyNString proxyName = proxyPkg + proxyClassNamePrefix + num;...// 生成代理类的class字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

不难看出,生成的代理类的类名是$ProxyN的形式,所以我们经常会看到$Proxy0这个类,就是动态代理在运行时生成的。

既然知道了动态代理是怎么生成代理类的了,那我们不妨把它生成的类打印出来看看,到底里面是怎么实现的。

// 调用Java生成字节码文件的方法byte[] proxyClassFile = ProxyGenerator.generateProxyClass(        "com.test.$proxy0.class", new Class[]{Teacher.class}, Modifier.FINAL);// 输出文件到本地FileOutputStream out = new FileOutputStream(new File("/temp/$Proxy0.class"));out.write(proxyClassFile);out.flush();out.close();

用java反编译软件打开生成的$Proxy0.class文件,内容如下:

package com.test.$proxy0;import com.demos.java.basedemo.proxy.bean.Teacher;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;final class class extends Proxy  implements Teacher{  private static Method m1;  private static Method m2;  private static Method m3;  private static Method m0;  public class(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }  public final boolean equals(Object paramObject)    throws   {    try    {      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  public final String toString()    throws   {    try    {      return (String)this.h.invoke(this, m2, null);    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  public final void teach()    throws   {    try    {      this.h.invoke(this, m3, null);      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  public final int hashCode()    throws   {    try    {      return ((Integer)this.h.invoke(this, m0, null)).intValue();    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  static  {    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.demos.java.basedemo.proxy.bean.Teacher").getMethod("teach", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      return;    }    catch (NoSuchMethodException localNoSuchMethodException)    {      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    }    catch (ClassNotFoundException localClassNotFoundException)    {      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    }  }}

可以看出,代理类不仅代理了teach()这个方法,还代理了toString()、equals()和hashCode()方法,因为java中所有的类都是继承Object类的,所以自然有这些方法。其中还有一个地方要特别注意:生成的代理类继承了Proxy这个类,因此它只能通过实现接口来代理其他的类,现在知道为什么Java动态代理只能代理接口了。

既然都讲到这个份上了,当然不能不继续深入了。接下来是真正压轴的环节,实现自己的动态代理类。

手动实现动态代理

首先分析一下实现动态代理需要的步骤:

1.拼接java类实现,并生成class字节码2.加载class字节码到JVM3.实现自己的InvocationHandler处理器4.对外提供接口

既然知道了Java动态代理的原理,我们不妨借鉴Java生成的class文件格式,同时去掉默认继承的Proxy,使得我们自己的动态代理既可以代理接口,也可以代理类。先写出代理类的格式(假设代理的是类TeacherChan):

public class $Proxy0 extends TeacherChan {private InvocationHandler handler;private static Method m0;static {    try {        // 利用反射获取TeacherChan的teach()方法        m0 = TeacherChan.class.getMethod("teach", new Class[]{});    } catch (NoSuchMethodException ne) {        throw new NoSuchMethodError(ne.getMessage());    }}// 构造方法中传入代理类处理器public $Proxy0(InvocationHandler handler) {    this.handler = handler;}public void teach() {    try {        // 收集teach()方法传入的参数,此处参数为空        Object[] args = new Object[]{};        // 执行代理类的teach()        Object result = handler.invoke(this, m0, args);        // 如果有返回值,此处要返回result    } catch (Error|RuntimeException e) {        throw e;    } catch (Throwable t) {        throw new UndeclaredThrowableException(t);    }}}

好了,生成类的格式大概就是这样设计,实际编写代码时需要处理参数、返回值和异常的情况,略微有点繁琐。下面是动态类生成器:

public class MyProxyGenerator {    // 换行符    public static final String LINE_SEPARATOR = "\r\n";    // 动态代理类包名    public static final String PROXY_CLASS_PACKAGE = "com.demos.proxy";    // 动态代理类名前缀    public static final String PROXY_CLASS_NAME_PREFIX = "$Proxy";    // 动态代理类文件索引    public static final AtomicLong INDEX_GENERATOR = new AtomicLong();    // 动态代理生成文件临时目录    public static final String PROXY_CLASS_FILE_PATH = "/temp";    /**     * 生成代理类并加载到JVM     * @param clazz     * @param methods     * @throws Exception     */    public static Class<?> generateAndLoadProxyClass(Class<?> clazz, Method[] methods) throws Exception {        long index = INDEX_GENERATOR.getAndIncrement();        // 代理类类名        String className = PROXY_CLASS_NAME_PREFIX + index;        String fileName = PROXY_CLASS_FILE_PATH + File.separator + className + ".java";        FileWriter writer = null;        try {            // 生成.java文件            writer = new FileWriter(new File(fileName));            writer.write(generateClassCode(PROXY_CLASS_PACKAGE, className, clazz, methods));            writer.flush();            // 编译.java文件            compileJavaFile(fileName);            // 加载class到JVM            String classPath = PROXY_CLASS_FILE_PATH + File.separator + className + ".class";            Class<?> proxyClass = MyClassLoader.getInstance().findClass(classPath, PROXY_CLASS_PACKAGE + "." + className);            return proxyClass;        } finally {            if (writer != null) {                writer.close();            }        }    }    /**     * 编译.java文件     * @param fileName     * @throws IOException     */    private static void compileJavaFile(String fileName) throws IOException {        compileByTools(fileName);//        compileByExec(fileName);    }    /**     * 使用Runtime执行javac命令     * 注意: 需要指定classpath, 否则找不到依赖的类     * 建议使用compileByTools()     * @param fileName     * @throws IOException     */    @Deprecated    private static void compileByExec(String fileName) throws IOException {        // 获取当前的classpath        String classpath = MyProxyGenerator.class.getResource("/").getPath();        // 运行命令: javac -classpath ${classpath} ${filepath}        String command = "javac -classpath " + classpath + " " + fileName;        Process process = Runtime.getRuntime().exec(command);        // 等待执行, 并输出错误日志        try {            InputStream errorStream = process.getErrorStream();            InputStreamReader inputStreamReader = new InputStreamReader(errorStream);            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);            String line = null;            while ((line = bufferedReader.readLine()) != null) {                System.out.println(line);            }            int exitVal = process.waitFor();            System.out.println("Process exitValue: " + exitVal);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    /**     * 使用JDK自带的JavaCompiler     * @param fileName     * @throws IOException     */    private static void compileByTools(String fileName) throws IOException {        // 获取系统Java编译器        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();        // 获取标准文件管理器实例        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);        try {            Iterable units = fileManager.getJavaFileObjects(fileName);            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);            task.call();        } finally {            fileManager.close();        }    }    /**     * 拼接 class 代码片段     * @param packageName   代理类包名     * @param clazz         要代理的类型     * @return     */    private static String generateClassCode(String packageName, String className, Class<?> clazz, Method[] methods) throws Exception {        StringBuilder classCodes = new StringBuilder();        /*--------------------包名和依赖 start--------------------*/        classCodes.append("package ").append(packageName).append(";").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        classCodes.append("import java.lang.reflect.*;").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        /*--------------------包名和依赖 start--------------------*/        /*--------------------类定义 start--------------------*/        classCodes.append("public class ").append(className);        if (clazz.isInterface()) {            classCodes.append(" implements ");        } else {            classCodes.append(" extends ");        }        classCodes.append(clazz.getName()).append(" {").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        /*--------------------类定义 end--------------------*/        /*--------------------声明变量InvocationHandler start--------------------*/        classCodes.append("private InvocationHandler handler;").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        /*--------------------声明变量InvocationHandler end--------------------*/        /*--------------------声明代理方法 start--------------------*/        for (int i = 0; i < methods.length; i++) {            classCodes.append("private static Method m").append(i).append(";").append(LINE_SEPARATOR);        }        classCodes.append(LINE_SEPARATOR);        /*--------------------声明代理方法 end--------------------*/        /*--------------------代理方法对象初始化 start--------------------*/        classCodes.append("static {").append(LINE_SEPARATOR);        classCodes.append("    ").append("try {").append(LINE_SEPARATOR);        for (int i = 0; i < methods.length; i++) {            Method method = methods[i];            classCodes.append("    ").append("    ").append("m").append(i).append(" = ").append(clazz.getName())                    .append(".class.getMethod(\"").append(method.getName()).append("\", new Class[]{");            // 方法参数            Parameter[] params = method.getParameters();            if (params.length != 0) {                for (int j = 0; j < params.length; j++) {                    if (j != 0) {                        classCodes.append(", ");                    }                    Parameter param = params[j];                    classCodes.append(param.getType().getName()).append(".class");                }            }            classCodes.append("});").append(LINE_SEPARATOR);        }        classCodes.append("    ").append("} catch (NoSuchMethodException ne) {").append(LINE_SEPARATOR);        classCodes.append("    ").append("    ").append("throw new NoSuchMethodError(ne.getMessage());").append(LINE_SEPARATOR);        classCodes.append("    ").append("}").append(LINE_SEPARATOR);        classCodes.append("}").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        /*--------------------代理方法对象初始化 end--------------------*/        /*--------------------定义构造函数 start--------------------*/        classCodes.append("public ").append(className).append("(InvocationHandler handler) {").append(LINE_SEPARATOR);        classCodes.append("    ").append("this.handler = handler;").append(LINE_SEPARATOR);        classCodes.append("}").append(LINE_SEPARATOR);        classCodes.append(LINE_SEPARATOR);        /*--------------------定义构造函数 end--------------------*/        /*--------------------填充其他函数 start--------------------*/        classCodes.append(generateMethodCode(clazz, methods));        /*--------------------填充其他函数 end--------------------*/        // 类结束        classCodes.append("}").append(LINE_SEPARATOR);        return classCodes.toString();    }    /**     * 拼接 method 代码片段     * @param clazz     * @param methods     * @return     * @throws Exception     */    private static String generateMethodCode(Class<?> clazz, Method[] methods) throws Exception {        StringBuilder methodCodes = new StringBuilder();        for (int i = 0; i < methods.length; i++) {            Method method = methods[i];            // 返回类型            String returnType = method.getReturnType().getName();            // 参数列表            Parameter[] params = method.getParameters();            // 异常列表            Class<?>[] exceptionTypes = method.getExceptionTypes();            /*--------------------方法定义 start--------------------*/            methodCodes.append("public ").append(returnType).append(" ").append(method.getName());            methodCodes.append("(");            // 填充参数            if (params.length != 0) {                for (int j = 0; j < params.length; j++) {                    if (j != 0) {                        methodCodes.append(", ");                    }                    Parameter param = params[j];                    methodCodes.append(param.getType().getName()).append(" ").append(param.getName());                }            }            methodCodes.append(")");            // 填充异常            if (exceptionTypes.length != 0) {                methodCodes.append(" throws ");                for (int j = 0; j < exceptionTypes.length; j++) {                    if (j != 0) {                        methodCodes.append(", ");                    }                    methodCodes.append(exceptionTypes[j].getName());                }            }            methodCodes.append(" {").append(LINE_SEPARATOR);            /*--------------------方法定义 end--------------------*/            /*--------------------方法体 start--------------------*/            methodCodes.append("    ").append("try {").append(LINE_SEPARATOR);            // 方法参数            methodCodes.append("    ").append("    ").append("Object[] args = new Object[]{");            if (params.length != 0) {                for (int j = 0; j < params.length; j++) {                    if (j != 0) {                        methodCodes.append(", ");                    }                    Parameter param = params[j];                    methodCodes.append(param.getName());                }            }            methodCodes.append("};").append(LINE_SEPARATOR);            // 执行InvocationHandler.invoke()            methodCodes.append("    ").append("    ").append("Object result = handler.invoke(this, m").append(i)                    .append(", args);").append(LINE_SEPARATOR);            // 返回结果            if (!"void".equals(returnType)) {                methodCodes.append("    ").append("    ").append("return (").append(returnType).append(") result;").append(LINE_SEPARATOR);            }            // 异常处理            methodCodes.append("    ").append("} catch (Error|RuntimeException");            for (Class<?> exceptionType : exceptionTypes) {                methodCodes.append("|").append(exceptionType.getName());            }            methodCodes.append(" e) {").append(LINE_SEPARATOR);            methodCodes.append("    ").append("    ").append("throw e;").append(LINE_SEPARATOR);            methodCodes.append("    ").append("} catch (Throwable t) {").append(LINE_SEPARATOR);            methodCodes.append("    ").append("    ").append("throw new UndeclaredThrowableException(t);").append(LINE_SEPARATOR);            methodCodes.append("    ").append("}").append(LINE_SEPARATOR);            /*--------------------方法体 end--------------------*/            // 方法结束            methodCodes.append("}").append(LINE_SEPARATOR).append(LINE_SEPARATOR);        }        return methodCodes.toString();    }}

实际上只是拼接前面给出的代理类实现而已,代码量有点大,但并不难理解。

下一步,实现自己的类加载器,来加载生成的class字节码:

public class MyClassLoader extends ClassLoader {    private static MyClassLoader loader;    private MyClassLoader() {    }    public static MyClassLoader getInstance() {        if (loader == null) {            synchronized (MyClassLoader.class) {                // 得到锁首先检查loader是否已经存在, 避免重复创建                if (loader == null) {                    loader = new MyClassLoader();                }            }        }        return loader;    }    /**     * 加载class文件,并返回类型对象     *     * @param filePath     * @param className     * @return     * @throws ClassNotFoundException     */    public Class<?> findClass(String filePath, String className) throws ClassNotFoundException {        try {            // 读取指定class文件的字节码            byte[] classBytes = Files.readAllBytes(Paths.get(filePath));            // 加载类并返回class类型对象            Class<?> clazz = defineClass(className, classBytes, 0, classBytes.length);            return clazz;        } catch (IOException e) {            e.printStackTrace();        }        throw new ClassNotFoundException(className);    }}

到这一步,复杂的工作基本做完了,接下来只剩下自定义处理器类和对外接口了

自定义处理器类与Java动态代理的方式相同:

public class MyInvocationHandler implements InvocationHandler {    private Object proxied;    public MyInvocationHandler(Object object) {        this.proxied = object;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("-------------------老师好[by 自定义动态代理]-------------------");        Object obj = method.invoke(proxied, args);        System.out.println("-------------------老师再见[by 自定义动态代理]-------------------");        return obj;    }}

对外接口:

public class MyDynamicProxy {    public static <T> T newProxyInstance(Class<T> clazz, InvocationHandler handler) throws Exception {        // 要代理的方法: public & !final        Method[] proxyMethods = Arrays.stream(clazz.getMethods())                .filter(method -> !Modifier.isFinal(method.getModifiers()))                .collect(Collectors.toList())                .toArray(new Method[0]);        // 生成的代理类        Class<?> proxyClass = MyProxyGenerator.generateAndLoadProxyClass(clazz, proxyMethods);        // 代理类的构造方法        Constructor c = proxyClass.getConstructor(InvocationHandler.class);        // 创建代理类对象        Object proxyObj = c.newInstance(handler);        return (T) proxyObj;    }}

搞定,测试一下效果:

TeacherChan proxy1 = MyDynamicProxy.newProxyInstance(        TeacherChan.class, new MyInvocationHandler(new TeacherChan()));proxy1.teach();TeacherCang proxy2 = MyDynamicProxy.newProxyInstance(        TeacherCang.class, new MyInvocationHandler(new TeacherCang()));proxy2.teach();

输出:

-------------------老师好[by 自定义动态代理]-------------------大家好,我是陈老师,我教大家摄影!-------------------老师再见[by 自定义动态代理]--------------------------------------老师好[by 自定义动态代理]-------------------大家好,我是苍老师,我教大家生物!-------------------老师再见[by 自定义动态代理]-------------------

完美!大功告成!