java动态代理整理

作者: 的一幕 | 来源:发表于2019-08-14 17:26 被阅读108次

    说到java中的代理模式,相信很多人是比较耳熟,但是真正的让自己写肯定是不知道从何下手,因此该篇带着大家一起熟悉代理模式的使用,代理模式主要是通过一个包装类来操作真正工作的类,也就是说代理模式只是能完成一些辅助的功能,下面通过一个例子来说明静态代理和动态代理的使用:

    比如说当下卖的合资车,三大件还是国外生产的,而到了国内就进行组装,然后进行销售,可以说在国内只是进行了一个代理组装,下面通过这个场景来介绍静态代理和动态代理的。

    静态代理

    /**
     * 国外的工厂
     */
    public interface ForeignFactory {
        //生产发动机
        void produceEngine();
    
        //生产变速箱
        void productGearbox();
    
        //生产底盘
        void productChassis();
    
    }
    
    /**
     * 奥迪的车子
     */
    public class AudiQ5 implements ForeignFactory {
        @Override
        public void produceEngine() {
            System.out.println("德国生产的发动机");
        }
    
        @Override
        public void productGearbox() {
            System.out.println("德国生产的变速箱");
        }
    
        @Override
        public void productChassis() {
            System.out.println("德国生产的底盘");
        }
    }
    
    /**
     * 国内进行组装的工厂
     */
    public class ChinaFactory implements ForeignFactory {
        private AudiQ5 audiQ5;
    
        public ChinaFactory(AudiQ5 audiQ5) {
            this.audiQ5 = audiQ5;
        }
    
        @Override
        public void produceEngine() {
            audiQ5.produceEngine();
            System.out.println("国内进行组装发动机");
        }
    
        @Override
        public void productGearbox() {
            audiQ5.productGearbox();
            System.out.println("国内进行组装变速箱");
        }
    
        @Override
        public void productChassis() {
            audiQ5.productChassis();
            System.out.println("国内进行组装底盘");
        }
    }
    

    通过客户端运行:

    public class Client {
        public static void main(String[] args) {
            AudiQ5 audiQ5 = new AudiQ5();
            ChinaFactory chinaFactory = new ChinaFactory(audiQ5);
            chinaFactory.produceEngine();
            chinaFactory.productGearbox();
            chinaFactory.productChassis();
        }
    }
    

    日志如下:

    image.png
    可以看到这里就是一个典型的静态代理模式,ChinaFactory里面真正工作的其实是AudiQ5,但是代理类可以做一些初始化的操作,或者做一些收尾的工作,在该例子中,我们在ChinaFactory做的是收尾工作,都是一些三大件的组装。

    比如现在需要增加对合资车的其他零件的组装呢,是不是还得增加代理类中的方法呢,没错,这就是静态代理类中不足的地方,每次增加接口中的方法,都会需要改变代理类,所以下面来介绍动态代理是怎么执行的。

    动态代理

    java已经提供了动态代理的实现类InvocationHandler,在InvocationHandler类中需要传入被代理的类对象,重写invoke方法,在invoke方法中,需要我们通过反射调用代理类的方法。下面还是通过上面的例子来说说动态代理

    //动态代理类
    public class DynamicHandler implements InvocationHandler {
        //动态代理类需要被代理的实例
        private AudiQ5 audiQ5;
        
        public DynamicHandler(AudiQ5 audiQ5) {
            this.audiQ5 = audiQ5;
        }
    
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            //此处就是执行被代理类的方法
            Object invoke = method.invoke(audiQ5, objects);
            //这里为了演示上面静态代理的效果,做的一些日志,实际项目中根据逻辑处理
            String name = method.getName();
            if (name.equals("produceEngine")) {
                System.out.println("国内进行组装发动机");
            } else if (name.equals("productGearbox")) {
                System.out.println("国内进行组装变速箱");
            } else if (name.equals("productChassis")) {
                System.out.println("国内进行组装底盘");
            }
            return invoke;
        }
    }
    

    上面例子中通过传入的被代理实例,在invoke方法中通过反射调用被代理的方法,后面为了演示和静态代理一样的效果,加的一些日志。

    咋们来看看客户端如何使用动态代理:

    public class Client {
        public static void main(String[] args) {
            AudiQ5 audiQ5 = new AudiQ5();
            //静态代理
    //        ChinaFactory chinaFactory = new ChinaFactory(audiQ5);
    //        chinaFactory.produceEngine();
    //        chinaFactory.productGearbox();
    //        chinaFactory.productChassis();
    
            //动态代理
            //拿到被代理类的类加载器,关于类加载器其实就是能执行类的class文件,
            // 也就是说有了类的加载器能实现代理类的class文件生成
            ClassLoader classLoader = audiQ5.getClass().getClassLoader();
            //找到被代理类的所有接口类,因为代理类需要知道被代理类实现了那些接口,继而实现接口中的所有方法
            Class[] interfaces = audiQ5.getClass().getInterfaces();
            //此处就是我们要处理代理类的逻辑
            DynamicHandler proxyHandler = new DynamicHandler(audiQ5);
            //通过Proxy.newProxyInstance生成代理类对象
            Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
            //因为代理类是根据接口来的,所以直接可以拿来强转
            ForeignFactory foreignFactory = (ForeignFactory) newProxyInstance;
            foreignFactory.produceEngine();
            foreignFactory.productGearbox();
            foreignFactory.productChassis();
        }
    }
    

    在动态代理中通过Proxy.newProxyInstance方法生成代理对象,而需要的参数ClassLoader :被代理类的类加载器,通过它能生成代理类的class文件interfaces:被代理类的所有接口字节码,因为代理类需要知道被代理类实现了那些接口proxyHandler:它是我们处理代理类时候的逻辑,比如可以做一些初始化或收尾的逻辑,最后的object就是被代理类实现的接口类,跟上面静态代理类ChinaFactory是一个道理。拿到代理类之后,就可以调用相关的方法。
    在上面客户端生成代理类也可以这样写:

    image.png
    直接传入接口的字节码数组,如果被代理类实现了多个接口,那肯定是传入多个接口类型,这里大家可以自己尝试。

    大家可能好奇动态代理中代理类长啥样,其实我们是可以手动生成的,可以借助sun.misc.ProxyGenerator.generateProxyClass来生成对应的class文件:

    public class ProxyUtils {
        public static void generateClassFile(Class clazz, String proxyName) {
            //根据类信息和提供的代理类名称,生成字节码
            byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
            String paths = clazz.getResource(".").getPath();
            System.out.println(paths);
            FileOutputStream out = null;
    
            try {
                //保留到硬盘中
                out = new FileOutputStream(paths + proxyName + ".class");
                out.write(classFile);
                out.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    如果直接在android项目中,是找不到ProxyGenerator类的,需要通过java项目来尝试:

    AudiQ5 audiQ5 = new AudiQ5();
    ProxyUtils.generateClassFile(audiQ5.getClass(), "DynamicChinaFactory");
    
    image.png

    可以看出来,通过传入的类名已经生成了,可以看看生成的代理类:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.single.proxy.bus.ForeignFactory;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class DynamicChinaFactory extends Proxy implements ForeignFactory {
        private static Method m1;
        private static Method m5;
        private static Method m2;
        private static Method m4;
        private static Method m3;
        private static Method m0;
    
        public DynamicChinaFactory(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 productChassis() throws  {
            try {
                super.h.invoke(this, m5, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        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 productGearbox() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void produceEngine() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        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 {
                //通过静态代码块生成了所有的方法,包括object类默认的几个方法
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m5 = Class.forName("com.single.proxy.bus.ForeignFactory").getMethod("productChassis");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m4 = Class.forName("com.single.proxy.bus.ForeignFactory").getMethod("productGearbox");
                m3 = Class.forName("com.single.proxy.bus.ForeignFactory").getMethod("produceEngine");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    相信大家看了动态代理生成的代理类,也很一目了然,代理类是继承了Proxy并且实现了ForeignFactory,在静态代码块中通过反射找到所有的方法,然后在每一个方法中通过super.h.invoke调用每一个方法,而super.h实际上是Proxy中的InvocationHandler:

    image.png
    这也就解释了为什么动态代理中需要实现InvocationHandler,并且需要重写invoke方法。
    好了,关于动态代理已经介绍完了,想必大家通过上面例子已经知道动态代理是要比静态代理强大得多了吧。

    总结

    • 动态代理无需知道实现了哪些接口
    • 动态代理只需要实现InvocationHandler接口就行,并且在重写的invoke方法中通过反射调用被代理类的方法。
    • 动态代理在改变了接口继承关系后,也不用管代理类,只需要处理InvocationHandler中的invoke逻辑。

    相关文章

      网友评论

        本文标题:java动态代理整理

        本文链接:https://www.haomeiwen.com/subject/aolbjctx.html