• JDK动态代理



    动态代理:字节码重组,即在每一个方法调用前后加些代码

    Inter:

    package proxy.jdk;
    
    /**
     *
     */
    public interface Inter {
        void findPerson();
    }

    Boss:

    package proxy.jdk;
    
    /**
     * Boss 到inter上找Person
     *
     */
    public class Boss implements Inter {
        @Override
        public void findPerson() {
            System.out.println("我要找java架构师");
        }
    }

    WebApp:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 找Person的App
     */
    public class WebApp implements InvocationHandler {
        private Object target;//被代理对象的引用作为一个成员变量保存下来了
        //获取被代理人的个人资料
        public Object getInstance(Object target){
            this.target = target;
            Class<?> clazz = target.getClass();
            System.out.println("被代理的对象:"+clazz);
            return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object o = method.invoke(this.target,args);
            after();
            return o;
        }
    
        private void after() {
            System.out.println("---------------");
        }
    
        private void before() {
            System.out.println("找Person,找我WebApp");
            System.out.println("---------------");
        }
    }

    Test:

    package proxy.jdk;
    
    import sun.misc.ProxyGenerator;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    
    /**
        * 1.拿到被代理对象的引用,然后获取它的接口
        * 2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
        * 3.把被代理对象的引用也拿到了
        * 4.重新动态生成一个class字节码
        * 5.然后编译
     */
    public class Test {
        public static void main(String[] args){
            try {
                Inter obj = (Inter) new WebApp().getInstance(new Boss());
                System.out.println(obj.getClass());
                obj.findPerson();
    
                //获取字节码内容
                // 使用Jad反编译 
           // idea安装插件 Java Bytecode Decompiler即可
    //            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Inter.class});
    //            FileOutputStream os = new FileOutputStream("./$Proxy0.class");
    //            os.write(bytes);
    //            os.close();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    反编译后的代码

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import com.vip.proxy.jdk.Inter;
    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 Inter {
        private static Method m1;
        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 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 findPerson() 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 {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.vip.proxy.jdk.Inter").getMethod("findPerson");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    View Code

    发现$Proxy0 继承了 Proxy 类,同时还实现了我们的 Inter接口,而且重写了findPerson()等方法。而且在静态块中用反射查找到了目标对象的所有方法,而且保存了所 有方法的引用,在重写的方法用反射调用目标对象的方法

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    手动实现一个?

    MyInvocationHandler:

    package proxy.myproxy;
    
    import java.lang.reflect.Method;
    
    public interface MyInvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    
    }

    MyClassLoader:

    package proxy.myproxy;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    /**
     * 代码生成、编译、重新动态load到JVM
     */
    public class MyClassLoader extends ClassLoader {
        private File baseDir;
    
        public MyClassLoader() {
            String path = MyClassLoader.class.getResource("").getPath();
            this.baseDir = new File(path);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String className = MyClassLoader.class.getPackage().getName() + "." + name;
            if (baseDir!=null){
                File classFile = new File(baseDir,name+".class");
                if (classFile.exists()){
                    FileInputStream in = null;
                    ByteArrayOutputStream out = null;
                    try {
                        in = new FileInputStream(classFile);
                        out = new ByteArrayOutputStream();
                        byte[] bytes = new byte[1024];
                        int len;
                        while ((len=in.read(bytes))!=-1){
                            out.write(bytes,0,len);
                        }
                        return defineClass(className,out.toByteArray(),0,out.size());
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        classFile.delete();
                        if (null!=in){
                            try {
                                in.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (null!=out){
                            try {
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            return null;
        }
    }

    MyProxy:

    package proxy.myproxy;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * 生成代理对象
     */
    public class MyProxy {
        public static Object newProxyInstance(MyClassLoader loader,
                                              Class<?>[] interfaces,
                                              MyInvocationHandler h) {
    
            try {
                String proxySrc = generateSrc(interfaces[0]);
                //将生成的源代码输出到磁盘,保存为.java文件
                String path = MyProxy.class.getResource("").getPath();
                File file = new File(path+"$Proxy0.java");
                FileWriter fw = new FileWriter(file);
                fw.write(proxySrc);
                fw.flush();
                fw.close();
                //编译源代码,并且生成.class文件
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
                Iterable iterable = manager.getJavaFileObjects(file);
    
                JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
                task.call();
                manager.close();
                //将class文件中的内容,动态加载到JVM中来
                Class<?> proxyClass = loader.findClass("$Proxy0");
                //返回被代理后的代理对象
                //1 获取构造
                Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
                file.delete();
                //2 返回实例
                return c.newInstance(h);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static String ln = "
    ";
        //生成源代码
        //参考反编译代码
        private static String generateSrc(Class<?> interfaces){
            StringBuffer src = new StringBuffer();
            String name = MyClassLoader.class.getPackage().getName();
            src.append("package "+name+";"+ln);
            src.append("import java.lang.reflect.Method;"+ln);
            src.append("public class $Proxy0 implements "+interfaces.getName());
            src.append("{"+ln);
            src.append("MyInvocationHandler invocationHandler;"+ln);
            src.append("public $Proxy0(MyInvocationHandler invocationHandler) {"+ln);
            src.append("this.invocationHandler = invocationHandler;"+ln);
            src.append("}"+ln);
            for (Method m:interfaces.getMethods()){
                src.append("public "+m.getReturnType()+" "+m.getName()+"(){"+ln);
                src.append("try{" + ln);
                src.append("Method m = " + interfaces.getName() + ".class.getMethod("" +m.getName()+"",new Class[]{});" + ln);
                src.append("this.invocationHandler.invoke(this,m,null);" + ln);
                src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
                src.append("}" + ln);
            }
            src.append("}");
    
            return src.toString();
        }
    
    }

    MyWebApp:

    package proxy.myproxy;
    
    import proxy.jdk.Inter;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     *
     */
    public class MyWebApp implements MyInvocationHandler {
        private Inter target;//被代理对象的引用作为一个成员变量保存下来了
    
        //获取被代理人的个人资料
        public Object getInstance(Inter target)throws Exception{
            this.target = target;
            Class clazz = target.getClass();
            System.out.println("被代理的对象:"+clazz);
            return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("找Person,找我MyWebApp");
            System.out.println("---------------");
            method.invoke(this.target,args);
            System.out.println("---------------");
            return null;
        }
    }

    Test:

    package proxy.myproxy;
    
    import proxy.jdk.Boss;
    import proxy.jdk.Inter;
    
    /**
     *
     */
    public class Test {
        public static void main(String[] args){
            try {
                Inter obj = (Inter) new MyWebApp().getInstance(new Boss());
                obj.findPerson();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    #### JDK Proxy 采用字节重组,重新生的对象来替代原始的对象以达到动态代理 的目的。JDK Proxy 生成对象的步骤如下:

    1. 拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。

    2. JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接 口。
    3. 动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体 现)。

    4. 编译新生成的 Java 代码.class。
    5. 再重新加载到 JVM 中运行。

  • 相关阅读:
    正确使用Core Data多线程的3种方式
    iOS开发周报:新一代 iPhone 可能取消耳机插口,Facebook 发布 tvOS SDK
    Atomic:Android、iOS和网页的设计工具
    iOS应用架构谈(三):View层的组织和调用方案(下)
    SQL server 2008数据库的备份与还原(转)
    重定向技术
    Python 正则表达式应用【转载】
    Fidder 工具使用
    HTTP请求大全
    RobotFramework环境搭建
  • 原文地址:https://www.cnblogs.com/fly-book/p/10361520.html
Copyright © 2020-2023  润新知