彻底搞懂动态代理(上)

什么是代理模式?


如果用专业术语来解:为其他对象提供一种代理以控制对这个对象的访问。如果投影在生活中,它可以理解成中介 黄牛 经纪人等…


解决的问题:


在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。说白了就是在你代码前面插一段后面插一段。

Java动态代理实现方式:


  • JDK 自带的动态代理


  • Cglib动态代理


1. JDK 自带的动态代理


我以黄牛为例,黄牛刚开始了解该人需求,该人将信息(JAY演唱会门票)给予黄牛,黄牛给票。黄牛就是该买票人的代理。


1.1 People.java


注意这必须是一个接口,原因往下看。


  1. public interface People {
  2.    /**
  3.     * 交谈
  4.     */
  5.    void speak();
  6. }


这个接口很简单,就是一个讲话的功能,但是它为什么必须是一个接口呢。因为在HuangNiu这个类中,Proxy.newProxyInstance 这个方法的实现需要接口,这一点我在HuangNiu类下解释的很清楚,往下看。


1.2 HuangNiu.java


黄牛代理类,获取到People信息后调用Proxy来生成一个新的代理类,它必须实现InvocationHandler接口,这个接口使得它可以通过invoke方法实现对真实角色(People)的代理访问。


  1. public class HuangNiu  implements InvocationHandler {

  2.    private People people;
  3.    /**
  4.     * 获取被代理对象信息
  5.     */
  6.    public Object getInstance(People people){
  7.        this.people = people;
  8.        Class clazz = people.getClass();
  9.        System.out.println("没生成代理之前的class对象:"+clazz );
  10.        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
  11.    }

  12.    @Override
  13.    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  14.        System.out.println("代理中...");
  15.        method.invoke(people);
  16.        System.out.println("代理处理完毕,OK,请查收");
  17.        return null;
  18.    }
  19. }


在实例化HuangNiu这个对象的时候,我们调用了Proxy的newProxyInstance方法:


  1. return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);


其中clazz是People的class对象。再来看看newProxyInstance的源码实现:


  1. public static Object newProxyInstance(ClassLoader loader,
  2.                                          Class<?>[] interfaces,
  3.                                          InvocationHandler h){

  4.        final Class<?>[] intfs = interfaces.clone();

  5.        //Look up or generate the designated proxy class.

  6.        Class<?> cl = getProxyClass0(loader, intfs);

  7.        //获取代理类的构造函数对象
  8.        //参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
  9.        final Constructor<?> cons = cl.getConstructor(constructorParames);
  10.        final InvocationHandler ih = h;
  11.        //根据代理类的构造函数对象来创建代理类对象
  12.        return newInstance(cons, ih);
  13. }


getProxyClass0方法源码:


  1.    private static Class<?> getProxyClass0(ClassLoader loader,
  2.                                           Class<?>... interfaces) {
  3.        if (interfaces.length > 65535) {
  4.            throw new IllegalArgumentException("interface limit exceeded");
  5.        }

  6.        // If the proxy class defined by the given loader implementing
  7.        // the given interfaces exists, this will simply return the cached copy;
  8.        // otherwise, it will create the proxy class via the ProxyClassFactory
  9.        return proxyClassCache.get(loader, interfaces);
  10.    }


如果缓存中有该代理类,则取缓存,如果没有,则通过ProxyClassFactory来创建代理类。如果对如何生成代理类感兴趣则追踪下去即可。


我只取了核心代码和注释,可以看到JDK的动态代理实现是依据接口来重新生成一个新的代理类,


什么是新的代理类?

通俗点说就是综合和前后代理逻辑并重新生成一份.class文件来实现动态代理的类,下面也会具体说。


1.3 Me.java


被代理对象,实现了People接口,给代理提供需要的信息来实现被代理。


  1. public class Me implements People {

  2.    private String name;
  3.    private String type;

  4.    Me(String name, String type){
  5.        this.name = name;
  6.        this.type = type;
  7.    }
  8.    @Override
  9.    public void speak() {
  10.        System.out.println("我叫"+name+", 我要一张"+type);
  11.    }
  12. }


1.4 Main.java


  1. public class Main {
  2.    public static void main(String[] args) {
  3.        People instance = (People)new HuangNiu().getInstance(new Me("Fantj", "JAY演唱会门票"));
  4.        instance.speak();
  5.        System.out.println("生成代理对象后对象变成:"+instance.getClass());
  6.    }
  7. }

执行结果:

  1. 没生成代理之前的class对象:class com.fantj.proxy.jdk.Me
  2. 代理中...
  3. 我叫Fantj, 我要一张JAY演唱会门票
  4. 代理处理完毕,OK,请查收
  5. 生成代理对象后对象变成:class com.sun.proxy.$Proxy0


为了证明事实上真的有代理类的产生,我在代理完成前和代理完成后分别打印出它的类信息,可以看出是不同的,可以猜想到代理中是有代理类产生的,这个代理类就是$Proxy0。


那既然知道了这个类的信息,我们就可以逆向生成这个.class文件来看看(在main方法后追加):


  1. /**
  2. * 生成代码
  3. */
  4. try {
  5.    byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{instance.getClass()});
  6.    String path = Main.class.getResource("").toString();
  7. //            System.out.println("get the path"+path);
  8.    FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
  9.    fileOutputStream.write($Proxy0s);
  10.    fileOutputStream.close();
  11. } catch (IOException e) {
  12.    e.printStackTrace();
  13. }


它默认生成在项目根目录下: 我使用的IDEA工具会自动反编译.class文件为java代码,直接打开即刻看到源码,如果用别的工具的可以下载反编译工具来进行反编译。


$Proxy0.class


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

  5. import com.sun.proxy..Proxy0;
  6. import java.lang.reflect.InvocationHandler;
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Proxy;
  9. import java.lang.reflect.UndeclaredThrowableException;

  10. public final class $Proxy0 extends Proxy implements Proxy0 {
  11.    private static Method m1;
  12.    private static Method m5;
  13.    private static Method m2;
  14.    private static Method m4;
  15.    private static Method m11;
  16.    private static Method m13;
  17.    private static Method m0;
  18.    private static Method m10;
  19.    private static Method m12;
  20.    private static Method m6;
  21.    private static Method m9;
  22.    private static Method m3;
  23.    private static Method m7;
  24.    private static Method m8;

  25.    public $Proxy0(InvocationHandler var1) throws  {
  26.        super(var1);
  27.    }

  28.    public final boolean equals(Object var1) throws  {
  29.        try {
  30.            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
  31.        } catch (RuntimeException | Error var3) {
  32.            throw var3;
  33.        } catch (Throwable var4) {
  34.            throw new UndeclaredThrowableException(var4);
  35.        }
  36.    }

  37.    public final InvocationHandler getInvocationHandler(Object var1) throws IllegalArgumentException {
  38.        try {
  39.            return (InvocationHandler)super.h.invoke(this, m5, new Object[]{var1});
  40.        } catch (RuntimeException | Error var3) {
  41.            throw var3;
  42.        } catch (Throwable var4) {
  43.            throw new UndeclaredThrowableException(var4);
  44.        }
  45.    }

  46.    public final String toString() throws  {
  47.        try {
  48.            return (String)super.h.invoke(this, m2, (Object[])null);
  49.        } catch (RuntimeException | Error var2) {
  50.            throw var2;
  51.        } catch (Throwable var3) {
  52.            throw new UndeclaredThrowableException(var3);
  53.        }
  54.    }

  55.    public final Class getProxyClass(ClassLoader var1, Class[] var2) throws IllegalArgumentException {
  56.        try {
  57.            return (Class)super.h.invoke(this, m4, new Object[]{var1, var2});
  58.        } catch (RuntimeException | Error var4) {
  59.            throw var4;
  60.        } catch (Throwable var5) {
  61.            throw new UndeclaredThrowableException(var5);
  62.        }
  63.    }

  64.    public final Class getClass() throws  {
  65.        try {
  66.            return (Class)super.h.invoke(this, m11, (Object[])null);
  67.        } catch (RuntimeException | Error var2) {
  68.            throw var2;
  69.        } catch (Throwable var3) {
  70.            throw new UndeclaredThrowableException(var3);
  71.        }
  72.    }

  73.    public final void notifyAll() throws  {
  74.        try {
  75.            super.h.invoke(this, m13, (Object[])null);
  76.        } catch (RuntimeException | Error var2) {
  77.            throw var2;
  78.        } catch (Throwable var3) {
  79.            throw new UndeclaredThrowableException(var3);
  80.        }
  81.    }

  82.    public final int hashCode() throws  {
  83.        try {
  84.            return (Integer)super.h.invoke(this, m0, (Object[])null);
  85.        } catch (RuntimeException | Error var2) {
  86.            throw var2;
  87.        } catch (Throwable var3) {
  88.            throw new UndeclaredThrowableException(var3);
  89.        }
  90.    }

  91.    public final void wait() throws InterruptedException {
  92.        try {
  93.            super.h.invoke(this, m10, (Object[])null);
  94.        } catch (RuntimeException | InterruptedException | Error var2) {
  95.            throw var2;
  96.        } catch (Throwable var3) {
  97.            throw new UndeclaredThrowableException(var3);
  98.        }
  99.    }

  100.    public final void notify() throws  {
  101.        try {
  102.            super.h.invoke(this, m12, (Object[])null);
  103.        } catch (RuntimeException | Error var2) {
  104.            throw var2;
  105.        } catch (Throwable var3) {
  106.            throw new UndeclaredThrowableException(var3);
  107.        }
  108.    }

  109.    public final Object newProxyInstance(ClassLoader var1, Class[] var2, InvocationHandler var3) throws IllegalArgumentException {
  110.        try {
  111.            return (Object)super.h.invoke(this, m6, new Object[]{var1, var2, var3});
  112.        } catch (RuntimeException | Error var5) {
  113.            throw var5;
  114.        } catch (Throwable var6) {
  115.            throw new UndeclaredThrowableException(var6);
  116.        }
  117.    }

  118.    public final void wait(long var1) throws InterruptedException {
  119.        try {
  120.            super.h.invoke(this, m9, new Object[]{var1});
  121.        } catch (RuntimeException | InterruptedException | Error var4) {
  122.            throw var4;
  123.        } catch (Throwable var5) {
  124.            throw new UndeclaredThrowableException(var5);
  125.        }
  126.    }

  127.    public final void speak() throws  {
  128.        try {
  129.            super.h.invoke(this, m3, (Object[])null);
  130.        } catch (RuntimeException | Error var2) {
  131.            throw var2;
  132.        } catch (Throwable var3) {
  133.            throw new UndeclaredThrowableException(var3);
  134.        }
  135.    }

  136.    public final boolean isProxyClass(Class var1) throws  {
  137.        try {
  138.            return (Boolean)super.h.invoke(this, m7, new Object[]{var1});
  139.        } catch (RuntimeException | Error var3) {
  140.            throw var3;
  141.        } catch (Throwable var4) {
  142.            throw new UndeclaredThrowableException(var4);
  143.        }
  144.    }

  145.    public final void wait(long var1, int var3) throws InterruptedException {
  146.        try {
  147.            super.h.invoke(this, m8, new Object[]{var1, var3});
  148.        } catch (RuntimeException | InterruptedException | Error var5) {
  149.            throw var5;
  150.        } catch (Throwable var6) {
  151.            throw new UndeclaredThrowableException(var6);
  152.        }
  153.    }

  154.    static {
  155.        try {
  156.            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  157.            m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", Class.forName("java.lang.Object"));
  158.            m2 = Class.forName("java.lang.Object").getMethod("toString");
  159.            m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
  160.            m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass");
  161.            m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll");
  162.            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  163.            m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait");
  164.            m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify");
  165.            m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler"));
  166.            m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE);
  167.            m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("speak");
  168.            m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", Class.forName("java.lang.Class"));
  169.            m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE, Integer.TYPE);
  170.        } catch (NoSuchMethodException var2) {
  171.            throw new NoSuchMethodError(var2.getMessage());
  172.        } catch (ClassNotFoundException var3) {
  173.            throw new NoClassDefFoundError(var3.getMessage());
  174.        }
  175.    }
  176. }


里面大多是Object中的方法,还有我们定义的speak()方法,然后就是静态代码块(对变量进行初始化)。

上一篇:软件测试方法和技术


下一篇:mybatis-plus的使用 ------ 进阶