zoukankan      html  css  js  c++  java
  • JDK动态代理深入理解分析并手写简易JDK动态代理(上)

    原文同步发表至个人博客【夜月归途】

    原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html

    本博客关于Java动态代理相关内容直达链接:

    1. JDK动态代理浅析
    2. Cglib动态代理浅析
    3. JDK动态代理深入理解分析并手写简易JDK动态代理(上)
    4. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    博客真的是好几个月没更了,2019新年第一篇,继续深入动态代理,前两篇简单分析了动态代理的实现原理之后,这次继续深入了解具体的实现方式,并手写一套简易的动态代理已加强理解;
    先接上一篇代码,这里先上代码说话,一个简单案列,代理找对象和找工作:

    JDK动态代理只能代理有接口的类,定义Persion接口

    package com.guitu18.study.proxy;
    /**
     * @author zhangkuan
     * @email xianjian-mail@qq.com
     * @Date 2018/12/31 20:30
     */
    public interface Persion {
        /**
         * 找对象
         *
         * @param condition 对另一半的要求
         * @return 表态
         */
        String findLove(String condition);
        /**
         * 找工作
         */
        void findWord();
    }

    实现接口的类:

    package com.guitu18.study.proxy;
    /**
     * @author zhangkuan
     * @email xianjian-mail@qq.com
     * @Date 2018/12/31 20:13
     */
    public class ZhangKuan implements Persion {
        @Override
        public String findLove(String condition) {
            System.out.println(condition);
            return "叶青我爱你";
        }
        @Override
        public void findWord() {
            System.out.println("我想找月薪15-25k的工作");
        }
    }

    代理类:

    package com.guitu18.study.proxy.jdk;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
     * @author zhangkuan
     * @email xianjian-mail@qq.com
     * @Date 2018/12/31 20:13
     */
    public class JDKProxy {
        public static Object getProxyInstance(final Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // 方法执行前
                            System.out.println("开始事务...");
                            // 执行方法
                            Object invoke = method.invoke(target, args);
                            // 方法执行后
                            System.out.println("提交/回滚事务...");
                            return invoke;
                        }
                    });
        }
    }

    代理测试类:

    package com.guitu18.study.proxy.jdk;
    import com.guitu18.study.proxy.Persion;
    import com.guitu18.study.proxy.ZhangKuan;
    import sun.misc.ProxyGenerator;
    import java.io.FileOutputStream;
    /**
     * @author zhangkuan
     * @email xianjian-mail@qq.com
     * @Date 2018/12/31 20:28
     */
    public class JDKProxyTest {
        public static void main(String[] args) {
            try {
                Persion persion = (Persion) JDKProxy.getProxyInstance(new ZhangKuan());
                String love = persion.findLove("肤白貌美大长腿");
                System.out.println(love); // 叶青我爱你
                System.out.println(persion.getClass()); // class com.sun.proxy.$Proxy0
                /**
                 * 上面生成的代理对象字节码 com.sun.proxy.$Proxy0 是在内存中的
                 * 这里将其对象写到文件中,通过反编译查看
                 */
                byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Persion.class});
                FileOutputStream fos = new FileOutputStream("D://$Proxy0.class");
                fos.write(bytes);
                System.out.println("文件写入成功");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    到这里,代理任务已经可以完成看;此处,我们将JDK动态代理生成的代理类,通过流的形式保存到磁盘中了,现在使用idea自带反编译工具查看:

    import com.guitu18.study.proxy.Persion;
    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 Persion {
        private static Method m1;
        private static Method m2;
        private static Method m4;
        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 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 findWord() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String findLove(String var1) throws  {
            try {
                return (String)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"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m4 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findWord");
                m3 = Class.forName("com.guitu18.study.proxy.Persion").getMethod("findLove", 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());
            }
        }
    }

    看完这段代码是不是恍然大悟的感觉,生成的代理类继承了Proxy类并实现了我们的接口,并生成了被代理类的全部方法,包括toString、equals等等所有方法; 这里生成的代理类继承自Proxy,在Proxy中有这么一段代码:

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    这就是为什么我们在生成代理类的时候,要传入的一个InvocationHandler的实现,我们所有的代理增强代码都写在invoke()方法中,而最终代理类执行代理也是通过调用父类Proxy中的InvocationHandler接口的invoke()方法;
    而我们知道Java是单继承的,所以代理类只能去实现我们的接口以供我们可以将其强转;
    在我们调用自身方法时,代理类巧妙的通过调用生成的同名方法然后调用super.h.invoke()方法,最终就到了我们实现InvocationHandler时所写的invoke()增强方法;在此时,method.invoke()才真正通过反射调用了我们自身所写的方法;这种极为巧妙无侵入式的方法增强实在是妙,令人叹为观止;

    这里推荐一篇 劳夫子 的 [ ProxyGenerator生成代理类的字节码文件解析 ];
    分析的是ProxyGenerator.generateClassFile()字节码生成原理,写的非常好,值得好好看一下;

    下一篇,通过手写一套简易的JDK动态代理的实现增强理解;

  • 相关阅读:
    MINA简单的介绍
    java classloader详解
    nginx 和 tomcat 组合搭建后端负载均衡
    nginx主要配置
    Mysql知识汇总笔记
    gradle 构建java工程
    决策树
    如何使用hadoop RPC机制
    PowerPoint插入公式快捷键
    C++基础
  • 原文地址:https://www.cnblogs.com/guitu18/p/10224660.html
Copyright © 2011-2022 走看看