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


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

    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);
  • 相关阅读:
    PDO扩展
    阿里云ECS VSFTP上传本地文件
    Nginx+lua_Nginx+GraphicsMagick来实现实时缩略图
    Mysql 5.6主从同步配置与解决方案
    windows安装配置mongodb及图形工具MongoVUE
    安装phpredis扩展以及phpRedisAdmin工具
    Redis安装配置以及开机启动
    CentOS安装Git服务器 Centos 6.5 + Git 1.7.1.0 + gitosis
    OpenStack 入门3
    Openstack 入门2
  • 原文地址:https://www.cnblogs.com/qq438649499/p/12204705.html
Copyright © 2020-2023  润新知