java动态代理 $Proxy0源码分析

动态代理简介

  • 本来是对被代理对象的函数的直接调用,现在却通过一个代理对象间接调用被代理对象的函数,在间接调用时,就可以增加我们想实现的功能(指InvocationHandler子类对象的invoke方法中的实现,可以随意添加我们想执行的代码)。
  • 代理对象和被代理对象看起来是一样的,因为它们都实现了同一个接口。这一点可以通过反编译字节码来看。
  • 一般情况是,先把java文件编译成class文件,然后类加载器加载这个class字节码文件,从而让JVM可以生成这个类的对象。但动态代理是,在内存中生成这些字节码,然后再正常地加载字节码,生成对象,但是你却从头到尾都感受不到这个类的存在。
  • 动态代理只能针对interface。

示例代码

InvocationHandler只是一个只有一个函数的接口,这还意味着Proxy.newProxyInstance的第三个参数可以使用lambda表达式。

package java.lang.reflect;

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

有一个接口coder,它有两个函数code和debug。javaCoder类实现了接口coder。

import java.lang.reflect.*;
interface coder{
    void code();
    void debug(String whichBug);
}

class javaCoder implements coder{
    public void code(){
        System.out.println("im coding java");
    }
    public void debug(String whichBug){
        System.out.println("im debuging java"+whichBug);
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;
    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("**** proxy: " + proxy.getClass() +
                ", method: " + method + ", args: " + args);
        if(args != null)
            for(Object arg : args)
                System.out.println("  " + arg);
        return method.invoke(proxied, args);
    }
}

class SimpleDynamicProxy {
    public static void consumer(coder who) {
        who.code();
        who.debug("空指针异常");
        System.out.println(who.toString());
        System.out.println(who.hashCode());
        System.out.println(who.equals(new Object()));
    }
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        javaCoder javaer = new javaCoder();
        consumer(javaer);
        System.out.println();
        // Insert a proxy and call again:
        coder proxy = (coder)Proxy.newProxyInstance(
                coder.class.getClassLoader(),
                new Class[]{ coder.class },
                new DynamicProxyHandler(javaer));
        consumer(proxy);
    }
}/*output:
im coding java
im debuging java空指针异常
javaCoder@45ee12a7
1173230247
false

**** proxy: class $Proxy0, method: public abstract void coder.code(), args: null
im coding java
**** proxy: class $Proxy0, method: public abstract void coder.debug(java.lang.String), args: [Ljava.lang.Object;@266474c2
  空指针异常
im debuging java空指针异常
**** proxy: class $Proxy0, method: public java.lang.String java.lang.Object.toString(), args: null
javaCoder@45ee12a7
**** proxy: class $Proxy0, method: public native int java.lang.Object.hashCode(), args: null
1173230247
**** proxy: class $Proxy0, method: public boolean java.lang.Object.equals(java.lang.Object), args: [Ljava.lang.Object;@6f94fa3e
  java.lang.Object@5e481248
false
*/

从用法上来看,InvocationHandler子类对象的invoke方法是动态代理调用函数的最后过程,这是我们可以自由设计的地方,比如可以根据Method对象的名字来得到proxy想要调用的方法。我们只管把InvocationHandler子类对象传给Proxy.newProxyInstance作为第三个参数。

如果javaCoder实现了多个接口,同时你也想代理到这些接口的所有方法,那么你应该如此改写:

coder proxy = (coder)Proxy.newProxyInstance(
        javaer.getClass().getClassLoader(),//此时应该获取与这个类相关的类加载器,而不是针对于某个接口
        javaer.getClass().getInterfaces() ,//获得这个类实现过的所有接口
        new DynamicProxyHandler(javaer));

自动生成的$Proxy0源码

在调用Proxy.newProxyInstance之前,加一句System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");,就会在项目的根目录生成一个$Proxy0.class,本来这是字节码文件不能直接查看的,但IDEA会自动帮我们反编译class文件。
在这里插入图片描述
下面是$Proxy0源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements coder {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

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

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

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

    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 void debug(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } 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);
        } 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"));
            m4 = Class.forName("coder").getMethod("code");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("coder").getMethod("debug", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
  • 从类定义final class $Proxy0 extends Proxy implements coder可以看出,java的动态代理只能代理接口interface。因为它已经继承了Proxy类,那么可变部分只有implements coder了,如果同时代理了两个接口,那么这里implements后面会跟两个。
  • 从此类的静态变量和静态初始化块可以看出,它们保存了coder接口的方法和Object的三个方法对应的Method对象。顺便能看出,getMethod方法有时有第二个参数,是因为第一个参数对应的方法有形参。
  • InvocationHandler子类对象通过构造函数里的super(var1);传给了父类的成员变量h,每次到了需要真正调用时则执行super.h.invoke()
posted @ 2019-09-01 18:29  allMayMight  阅读(946)  评论(0编辑  收藏  举报