Java动态代理

ThinkFault

1. 自己实现代理类

image.png

public interface BizDemo {

    void helloGirl(String name);

    void helloBoy(String name);
}


public class BizDemoImpl implements BizDemo{
    @Override
    public void helloGirl(String name) {
        System.out.println( name + "helloGirl in BizDemoImpl");
    }

    @Override
    public void helloBoy(String name) {
        System.out.println( name + "helloBoy in BizDemoImpl");
    }
}

public class BizDemoImplProxy implements  BizDemo {
    private BizDemo real;
    BizDemoImplProxy(BizDemo real) {
        this.real = real;
    }
    @Override
    public void helloGirl(String name) {
        System.out.println("proxy by BizDemoImplProxy");
        this.real.helloGirl(name);
    }
    @Override
    public void helloBoy(String name) {
        System.out.println("proxy by BizDemoImplProxy");
        this.real.helloBoy(name);
    }
}

    public static void main(String[] args) {
        BizDemo demo = new BizDemoImpl();
        demo.helloGirl("Lucy");
        BizDemo demoProxy = new BizDemoImplProxy(demo);
        demoProxy.helloGirl("Lucy");
   }
//输出
LucyhelloGirl in BizDemoImpl
proxy by BizDemoImplProxy
LucyhelloGirl in BizDemoImpl

2. 用java动态代理生成类

    1. 首先让我们忘掉对BizDemoImpl做代理,看看java自动生成代理类的效果

先实现一个InvocationHandler

public class ProxyHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy invoke." + args[0]);
        return null;
    }
}

如下可以看到,只要有BizDemo这个接口,就可以通过java动态代理生成一个类,这个类拥有接口BizDemo的一切方法,并且把所有方法调用,都透传到ProxyHandler的invoke方法中。

    public static void main(String[] args) {
        Class[] inf = new Class[] {BizDemo.class};
        BizDemo d =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), inf, new ProxyHandler());
        d.helloGirl("javaDynamicProxy");
        d.helloBoy("javaDynamicProxy boy");
}
//输出
proxy invoke.javaDynamicProxy
proxy invoke.javaDynamicProxy boy

尽管我们可以让java给我们动态生成一个类,这个类实现了BizDemo接口,但是这样的类没有太多的实际意义。我们的目的是代理BizDemoImpl,看看我们是怎么达到的

    1. 让代理产生实际意义,对BizDemoImpl做代理
      其实很简单,我们只要把BizDemoImpl实例塞到ProxyHandler里,在invoke方法再调一次不就行了吗,于是我们创新创建一个Handler: ProxyHandler1
public class ProxyHandler1 implements InvocationHandler {

    private BizDemo real;
    public ProxyHandler1(BizDemo real) {
        this.real = real;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy invoke." + args[0]);
        method.invoke(real, args);
        return null;
    }
}
    public static void main(String[] args) {
        BizDemo demo = new BizDemoImpl();
        Class[] inf = new Class[] {BizDemo.class};
        BizDemo d =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), inf, new ProxyHandler1(demo));
        d.helloGirl("javaDynamicProxy");
        d.helloBoy("javaDynamicProxy boy");
}
//输出
proxy invoke.javaDynamicProxy
javaDynamicProxyhelloGirl in BizDemoImpl
proxy invoke.javaDynamicProxy boy
javaDynamicProxy boyhelloBoy in BizDemoImpl

可以看到,动态代理生成的类,真正的代理了BizDemoImpl实例demo

单从上述例子看,用java动态代理,反而更复杂类,要引入InvocationHandler和java.lang.reflect.Proxy,但仔细想是有好处的,
第一,不管用户的interface有多少个方法,最终都代理收拢到InvocationHandler的invoke
第二,如果项目有很多interface要代理,代理类会成倍增加,难以维护

java动态代理类图如下,
$Proxxy0是java动态生成的类,这个类继承类Proxy实现了BizDemo接口,也就是上述的main中的d,对d的调用,先到$Proxy0, $Proxxy0委托给父类的Proxy的成员变量InvocationHandler 实例,即ProxyHandler1实例;而ProxyHandler1实例的invoke方法里最终调用了被代理的BizDemoImpl。
总之看起来很绕,框架类的代码往往牺牲直观来实现通用和简洁

image.png

3. 神秘的动态生成的代理类

所谓动态生成,就是java在运行时生成,这种类只在内存,当然也可以把它保存到文件,看看它神秘的面纱

    public static void main(String[] args) {
        BizDemo demo = new BizDemoImpl();
        Class[] inf = new Class[] {BizDemo.class};
        BizDemo d =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), inf, new ProxyHandler1(demo));
        d.helloGirl("javaDynamicProxy");
        d.helloBoy("javaDynamicProxy boy");

// 保存动态类到文件
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", BizDemoImpl.class.getInterfaces());
        try {
            File file = new File("Proxy0.class");
            FileOutputStream out = new FileOutputStream(file);
            out.write(classFile);
        } catch (Exception e) {

        }
|

动态类代码如下,重要的三个点

  1. 动态类$Proxy0 extends Proxy implements BizDemo,继承Proxy实现BizDemo
  2. 所有调实现BizDemo接口的方法,都委托到父类Proxy的 h,对这个例子,就是ProxyHandler1实例
  3. Proxy的成员h,是抽象接口InvocationHandler,具体类由用户自己定制,体现了,依赖抽象而不依赖具体实现类的常用设计思想。
说明:$Proxy0的0是java动态生成代理类的编号,如果有多个编号自动递增

public final class $Proxy0 extends Proxy implements BizDemo {
    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 helloGirl(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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 helloBoy(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("test.BizDemo").getMethod("helloGirl", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("test.BizDemo").getMethod("helloBoy", 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());
        }
    }
}

4. 生成代理类的老方法

    public static void main(String[] args) {
        BizDemo demo = new BizDemoImpl();
       //1. 获取代理类的Class对象
        Class<?> proxyClass = Proxy.getProxyClass(BizDemo.class.getClassLoader(), new Class[] {BizDemo.class});
        try {
            // 2. 获取构造函数反射
            Constructor con = proxyClass.getConstructor(InvocationHandler.class);
           //3. 通过反射创建类实例
            BizDemo demo1 = (BizDemo) con.newInstance(new ProxyHandler1(demo));
            demo1.helloGirl("proxyclass");
        } catch (Exception e) {
            e.printStackTrace();
        }
}

如果看源码可以得知Proxy.newProxyInstance其实是封装了上述过程。

5. 可能遇到的坑

    public static void main(String[] args) {
        BizDemo demo = new BizDemoImpl();
        Class[] inf = new Class[] {BizDemo.class};
        BizDemo d =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), inf, new ProxyHandler1(demo));
        BizDemo d1 =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), BizDemoImpl.class.getInterfaces(), new ProxyHandler1(demo));
        BizDemo d2 =  (BizDemo) Proxy.newProxyInstance(BizDemo.class.getClassLoader(), BizDemo.class.getInterfaces(), new ProxyHandler1(demo));
}

d和d1都没问题,d2会报错,因为BizDemo.class.getInterfaces()返回长度0的的Class数组,具体可以看getInterfaces的接口注释说明

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy invoke." + args[0]);
        method.invoke(real, args);
//        method.invoke(proxy, args);// 导致递归调用
        return null;
    }

invoke传入的proxy看起来用不到。在ProxyHandler1的invoke方法中,传入的proxy是$Proxy0,如果用method.invoke(proxy, args),会递归调用,最终导致递归栈溢出。

6. 参考文档

https://www.cnblogs.com/gonja...

阅读 511
10 声望
1 粉丝
0 条评论
10 声望
1 粉丝
宣传栏