• java动态代理


    package dynamic;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
    * 该代理类的内部属性是Object类型,实际使用的时候通过该对象的构造方法传递一个对象
    * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的
    * 将要执行的方法。方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实
    * 对象的方法前后加入自己的一些额外方法;
    * @author xh
    *
    */
    public class TestDynamicProxy {// 客户端调用的类
    public static void main(String[] args) {
    RealSubject realSubject = new RealSubject();
    InvocationHandler handler = new DynamicSubject(realSubject);
    System.out.println("handler "+handler);
    Class<?> classType = handler.getClass();
    // 下面的代码一次性生成代理
    Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(), handler);
    subject.request();
    }
    }

    interface Subject {
    public void request();
    }

    class RealSubject implements Subject {

    @Override
    public void request() {
    System.out.println("From real subject");

    }

    }

    class DynamicSubject implements InvocationHandler {
    private Object sub;

    public DynamicSubject(Object obj) {
    this.sub = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("before calling:" + method);
    method.invoke(sub, args);
    System.out.println("after calling:" + method);
    return null;
    }

    }

    输出结果:

    handler dynamic.DynamicSubject@15db9742

    before calling:public abstract void dynamic.Subject.request()

    From real subject

    after calling:public abstract void dynamic.Subject.request()

    subject.getClass()是: class dynamic.$Proxy0

     

    public class DynamicProxyHandler implements InvocationHandler {
        private Object realObject;
    
        public DynamicProxyHandler(Object realObject) {
            this.realObject = realObject;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //代理扩展逻辑
            System.out.println("proxy do");
    
            return method.invoke(realObject, args);
        }
    }
    

    这个Handler中的invoke方法中实现了代理类要扩展的公共功能。

    到这里,需要先看一下这个handler的用法:

    public static void main(String[] args) {
            RealObject realObject = new RealObject();
            Action proxy = (Action) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Action.class}, new DynamicProxyHandler(realObject));
            proxy.doSomething();
    }
    

    Proxy.newProxyInstance 传入的是一个ClassLoader, 一个代理接口,和我们定义的handler,返回的是一个Proxy的实例。

    仔细体会这个过程,其实有点类似我们在静态代理中提到的方案一,生成了一个包含我们扩展功能,持有RealObject引用,实现Action接口的代理实例Proxy。只不过这个Proxy不是我们自己写的,而是java帮我们生成的,有没有一点动态的味道。

    让我们再回顾一下代理三要素:真实对象:RealObject,代理接口:Action,代理实例:Proxy

    上面的代码实含义也就是,输入 RealObject、Action,返回一个Proxy。妥妥的代理模式。

    综上,动态生成+代理模式,也就是动态代理。
    网上搜了不少文章,到了这里,接下来就是和cglib等动态代理实现方法做一下横向比较。本文不做横向比较,为了不偏离主题,接下来做纵向挖掘。

     

    package dynamic;

     

    import java.lang.reflect.InvocationHandler;

    import java.lang.reflect.Method;

    import java.lang.reflect.Proxy;

    import java.util.List;

    import java.util.Vector;

     

    public class Vectorproxy implements InvocationHandler {

    private Object proxyobj;

     

    public Vectorproxy(Object proxyobj) {

    this.proxyobj = proxyobj;

    }

     

    public static Object factory(Object obj) {

    Class<?> classType = obj.getClass();

    return Proxy.newProxyInstance(classType.getClassLoader(), classType.getInterfaces(), new Vectorproxy(obj));

    }

     

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    System.out.println("before calling :" + method);

    if (null != args) {

    for (Object obj : args) {

    System.out.println(obj);

    }

    }

    Object object = method.invoke(proxyobj, args);

    System.out.println("after calling :" + method);

    return object;

    }

     

    public static void main(String[] args) {

    List v = (List) factory(new Vector());

    v.add("New");

    v.add("York");

    System.out.println(v);

    v.remove(0);

    System.out.println(v);

    }

    }

    打印结果:

    before calling :public abstract boolean java.util.List.add(java.lang.Object)

    New

    after calling :public abstract boolean java.util.List.add(java.lang.Object)

    before calling :public abstract boolean java.util.List.add(java.lang.Object)

    York

    after calling :public abstract boolean java.util.List.add(java.lang.Object)

    before calling :public java.lang.String java.lang.Object.toString()

    after calling :public java.lang.String java.lang.Object.toString()

    [New, York]

    before calling :public abstract java.lang.Object java.util.List.remove(int)

    0

    after calling :public abstract java.lang.Object java.util.List.remove(int)

    before calling :public java.lang.String java.lang.Object.toString()

    after calling :public java.lang.String java.lang.Object.toString()

    [York]

     

     

    看一下源码

    道理清楚了,但是这篇文章题目是搞懂,所以来看一下这个Proxy是如何自动被生成的。入口就在newProxyInstance方法,核心代码如下:

    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
    
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
    {
        Class<?> cl = getProxyClass0(loader, intfs);
        ...
        final Constructor<?> cons = cl.getConstructor(constructorParams);
    
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                cons.setAccessible(true);
                return null;
            }
            });
        }
    return cons.newInstance(new Object[]{h});
    }
    

    整体流程就是:

    1、生成代理类Proxy的Class对象。

    2、如果Class作用域为私有,通过 setAccessible 支持访问

    3、获取Proxy Class构造函数,创建Proxy代理实例。

    生成Proxy的Class文件

    生成Class对象的方法中,先是通过传进来的ClassLoader参数和Class[] 数组作为组成键,维护了一个对于Proxy的Class对象的缓存。这样需要相同Proxy的Class对象时,只需要创建一次。

    第一次创建该Class文件时,为了线程安全,方法进行了大量的处理,最后会来到ProxyClassFactory的apply方法中,经过以下流程:

    1、校验传入的接口是否由传入的ClassLoader加载的。

    2、校验传入是否是接口的Class对象。

    3、校验是否传入重复的接口。

    4、拼装代理类包名和类名,生成.class 文件的字节码。

    5、调用native方法,传入字节码,生成Class对象。

    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
    

    看一下第四步生成.class文件字节码的过程,主要分为两个阶段:

    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
    
    for (int i = 0; i < interfaces.length; i++) {
        Method[] methods = interfaces[i].getMethods();
        for (int j = 0; j < methods.length; j++) {
             addProxyMethod(methods[j], interfaces[i]);
        }
    }
    methods.add(this.generateConstructor());
    
     for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        for (ProxyMethod pm : sigmethods) { 
            fields.add(new FieldInfo(pm.methodFieldName,
                                       "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
            methods.add(pm.generateMethod());
        }
     }
    methods.add(generateStaticInitializer());
    

    第一个阶段的代码比较清晰,主要就是添加各种Method,比如toString()、equals,以及传入的代理接口中的方法。再添加一下构造方法以及静态初始化方法。这要构成了一个对象,存储生成Proxy的Class的一些信息。

    到了这里,已经把要构造的Proxy的方法基本定义完成了,接下来就要生成这个.class文件了。

     ByteArrayOutputStream bout = new ByteArrayOutputStream();
     DataOutputStream dout = new DataOutputStream(bout);
     dout.writeInt(0xCAFEBABE);
     ...
     dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
     ...
     return bout.toByteArray();
    

    看到这个CAFEBABE,就清楚第二阶段的内容了。CAFEBABE是Class文件的魔数,关于Class文件这个咖啡宝贝的魔数,相信做Java的人都知道。没错,第二阶段就是生成字节码。按JVM规范,写入Class文件中包括权限控制、方法表、字段表等内容,生成符合规范的Class文件。最后返回对应的字节码。

    字节码生成以后,通过调用native方法defineClass解析字节码,就生成了Proxy的Class对象。

    Proxy构造方法

    看一下Proxy的构造方法字节码生成部分:

    MethodInfo minfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V",ACC_PUBLIC);
    DataOutputStream out = new DataOutputStream(minfo.code);
    code_aload(0, out);
    code_aload(1, out);
    out.writeByte(opc_invokespecial);
    out.writeShort(cp.getMethodRef(superclassName,"<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
    ...
    

    关键在于,生成了一个参数为InvocationHandler的构造方法,code加载的是jvm方法区中的代码,然后通过invokespecial指令调用了父类构造方法。

    查看生成的Class文件

    上面利用字节码生成技术产生Class文件的过程,看起来可能比较晦涩,其实我们可以查看这个产生的Proxy到底是个什么样子。

    注意ProxyGenerator中有这样一个逻辑:

    if(saveGeneratedFiles) {
        ...
        FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class");
        file.write(classFile);
        ...
     }
    

    再看一下saveGeneratedFiles这个变量:

    private final static boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged( 
        new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))
        .booleanValue();
    

    这是一个final类型的变量,通过GetBooleanAction方法读取系统变量,获取系统设置。默认这个值是false,稍微看一下System这个类的源码,发现有可以设置系统变量的Api,然后在程序的main 函数设置一下这个变量:

    System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    

    这个时候,再跑一遍程序,就可以看到生成的Proxy的Class文件了,直接双击利用 ide 反编译。

    
    package com.sun.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements Action {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
    
        public final void doSomething() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        ...
    
        static {
            try {
               ...
                m3 = Class.forName("Action").getMethod("doSomething", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    

    省略一些无关代码,可以看到两个重要的方法。

    一个就是我们的代理方法doSomething、另一个就是构造方法。

    这个$Proxy0 继承 Proxy并调用了父类的构造方法,回忆一下上文提到的invokeSpecial,怎么样,对上了吧。

    看一下Proxy中这个构造方法:

        protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }
    

    在看一下$Proxy0 的代理方法:

    super.h.invoke(this, m3, (Object[])null);
    

    再来回顾一下生成Proxy实例的过程:

    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
    ...
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    ...
    return cons.newInstance(new Object[]{h});   

    其实newInstance生成Proxy实例时,通过$Proxy0的Class对象,选择了这个InvocationHandler为参数的构造方法,传入我们定义的InvocationHandler并生成了一个 Proxy0的实例!InvocationHandler 里有realObject的逻辑以及我们的扩展逻辑,当我们调用Proxy0的doSomething方法时,就会调用到我们InvocationHandler 里 实现的invoke方法。

    对上面这个过程,做一张图总结一下:

    flow.png

    package dynamic;
    import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 该代理类的内部属性是Object类型,实际使用的时候通过该对象的构造方法传递一个对象 * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的 * 将要执行的方法。方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实 * 对象的方法前后加入自己的一些额外方法; * @author xh * */public class TestDynamicProxy {// 客户端调用的类public static void main(String[] args) {RealSubject realSubject = new RealSubject();InvocationHandler handler = new DynamicSubject(realSubject);System.out.println("handler "+handler);Class<?> classType = handler.getClass();// 下面的代码一次性生成代理Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(), handler);subject.request();}}
    interface Subject {public void request();}
    class RealSubject implements Subject {
    @Overridepublic void request() {System.out.println("From real subject");
    }
    }
    class DynamicSubject implements InvocationHandler {private Object sub;
    public DynamicSubject(Object obj) {this.sub = obj;}
    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before calling:" + method);method.invoke(sub, args);System.out.println("after calling:" + method);return null;}
    }

  • 相关阅读:
    normalize.css 中文版
    [转载]自适应高度输入框
    【转载】H5页面列表的无线滚动加载(前端分页)
    CSS设置table下tbody滚动条与thead对齐的方法
    [转载]Jquery mobiscroll 移动设备(手机)wap日期时间选择插件以及滑动、滚动插件
    wordpress 目录、数据结构和解析原理
    WordPress基础知识:条件判断标签及用法大全
    主题如何添加tag标签页面
    WordPress进阶:[2]不同页面显示不同的侧边栏
    WordPress进阶:[1]怎样用tag标签做导航菜单
  • 原文地址:https://www.cnblogs.com/erma0-007/p/8654694.html
Copyright © 2020-2023  润新知