JVM@从字节码角度理解动态代理

从字节码角度理解动态代理

2019-08-23 16:50:38 更多

版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。本文链接:https://blog.csdn.net/weixin_38405354/article/details/100041929

从字节码角度理解动态代理

首先编写一个动态代理实例

interface Subject{
    void request();
}

class RealSubject implements Subject{
    public void request(){
        System.out.println("real subject");
    }
}

class DynamicSubject implements InvocationHandler{
    private Object subject;     // 这个就是我们要代理的真实对象
    public DynamicSubject(Object subject)             
    {
        this.subject = subject;        //    构造方法,给我们要代理的真实对象赋初值
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        // 在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent house");
        System.out.println("Method:" + method);
        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用        
        method.invoke(subject, args);
        // 在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent house");
        return null;
    }
}

public class Client {
    public static void main(String[] args) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", 
"true");            //将动态代理生成的class输出文件
        // 我们要代理的真实对象
        Subject realSubject = new RealSubject();//这里指定被代理类 
        // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicSubject(realSubject);
        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 
         * 第一个参数 handler.getClass().getClassLoader(),我们这里使用代理类的类加载器,也就是被代理的那个真实对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 
         * 第三个参数handler,我们这里将这个代理对象关联到了上方的InvocationHandler 这个对象上
         */
        Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), handler);
        subject.request();
    }
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

运行程序结果:
before rent house
Method:public abstract void controller.Subject.request()
real subject
after rent house

  • 通过改变sun.misc.ProxyGenerator.saveGeneratedFiles系统属性,可以将动态代理生成的class文件输出(默认只在内存,不会写到硬盘),得到Proxy0.class文件,使用反编译工具可以得到Proxy0.class文件,使用反编译工具可以得到Proxy0.class文件,使用反编译工具可以得到Proxy0的文件如下:
final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

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

    public final boolean equals(Object paramObject) {
        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() {
        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 request() {
        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() {
        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("controller.Subject").getMethod("request", 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());
        }
    }
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566

由反编译$Proxy0.class文件内容,可轻易得知:

  • 1.$Proxy0是Proxy的子类,实现Subject接口;
  • 2.$Proxy0重写了Object中的equal()、toString()、hashCode()方法;
  • 3.在调用$Proxy0中方法时,实际上调用父类Proxy的h属性的invoke()方法,其中h为Proxy.newInstance()方法传入的DynamicSubject,也就是说最终会调用DynamicSubject的invoke()方法,并将参数传入—this.h.invoke(this, m3, null);

猜你喜欢

转载自www.cnblogs.com/qq438649499/p/12204705.html