zoukankan      html  css  js  c++  java
  • 动态代理 原理简析(java. 动态编译,动态代理)

    动态代理:
      1.动态编译 JavaCompiler.CompilationTask 动态编译想理解自己查API文档
      2.反射被代理类 主要使用Method.invoke(Object o,Object... args);对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
      3.类的加载 URLClassLoader可以加载硬盘任意位置的.java文件。class.getClassLoader只能加载classPath目录下的类。
    动态代理可以理解为 动态生成发射代理的类。这其中可以动态增加逻辑操作。比如日志的打印,事物的处理等。spring的AOP操作也是动态代理的。

    假设我们有一个接口GrowAble可成长的。

    package com.cn;
    
    public interface GrowAble {
        void growUp();
    }

    一棵小树苗实现了这个接口

    package com.cn;
    public class Tree implements GrowAble {
        @Override
        public void growUp() {
            System.out.println("I am a tree , I'm grow up!");
        }
    
    }

    这时我们想不在不改变源码的情况下想知道树长了多少这个操作?
    我们需要一个转换接口。

    package com.cn;
    import java.lang.reflect.Method;
    
    public interface InvactionHandle {
        void invoke(Object o,Method m);
    }

    一个实现接口类。

    package com.cn;
    import java.lang.reflect.Method;
    import java.util.Random;
    
    public class HeightInvactionHandle implements InvactionHandle {
        @Override
        public void invoke(Object c, Method m) {
            try {
                m.invoke(this.o);
                System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        private Object o;
        public HeightInvactionHandle(Object o) {
            super();
            this.o = o;
        }
    }

    现在最重要的Proxy类了。把上述两个接口接口起来。

    package com.cn;
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.net.URLClassLoader;
    import javax.tools.JavaCompiler;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import javax.tools.JavaCompiler.CompilationTask;
    /**
     * 动态代理
     * @author 灵台方寸小道士
     */
    public class Proxy {
        public static Object getNewInstance(Class<?> c,Object object) throws Exception {
            String path = System.getProperty("user.dir") + File.separator + "mybin"
                    + File.separator + "com" + File.separator + "cn"
                    + File.separator;
            String fileName = "$Proxy.java";
            String nextLine = System.getProperty("line.separator");
            // create java File
            String fileValue = "package com.cn;"+nextLine+
                               "import com.cn.*;"+nextLine+
                               "import java.lang.reflect.Method;"+nextLine+
                               "public class $Proxy implements "+ c.getName() +"{"+nextLine+
                               "    private InvactionHandle h;"+nextLine+
                               "    public $Proxy(InvactionHandle hin)"+ nextLine+
                               "     {"+nextLine+
                               "           this.h = hin;"+nextLine+
                               "     }"+nextLine;
            Method[] methods = c.getDeclaredMethods();
            for (Method m:methods) {
                fileValue += "    public "+ m.getReturnType()+" "+m.getName()+"()"+nextLine+
                           "     {"+nextLine+
                           "          try{            "+nextLine+
                           //测试方法不带参数  所以new Class<?>[]{}空参数传入
                           "          Method me = "+c.getName()+".class.getDeclaredMethod(""+m.getName()+"",  new Class<?>[]{});"+nextLine+
                           "          h.invoke(this,me);"+nextLine+
                           "          }catch(Exception e){ "+nextLine+
                           "           e.printStackTrace(); }"+nextLine+
                           "     }"+nextLine;
            }
            fileValue +="}"+nextLine;
            File f =  new File(path);//是否存在此目录
            if (!f.exists())
                f.mkdirs();
            FileWriter writer = new FileWriter(new File(f,fileName));
            writer.write(fileValue);
            writer.flush();
            writer.close();
            System.out.println("***************     create java file over      ******************");
            // compiler 生成 class文件  调取javac编译
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null,null, null);
            Iterable<? extends JavaFileObject> in = manager.getJavaFileObjects(path+ fileName);
            CompilationTask task = compiler.getTask(null, manager, null, null,null, in);
            task.call();
            System.out.println("***************     complier class file over      ******************");
    
            // loader 加载class文件 的第一种方法 URLClassLoader可以load任意目录下的类!
            URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + File.separator + "mybin"+ File.separator) };
            URLClassLoader loader = new URLClassLoader(urls);
            Class<?> d = loader.loadClass("com.cn.$Proxy");
            System.out.println("***************     loader class file over      ******************");
    
            // newInstance class JVM
            Constructor<?> con = d.getConstructor(InvactionHandle.class);
            Object o = con.newInstance(object);
            // newInstance...
            /**
             加载class文件 的第二种方法 ClassLoader只能load位于classpath(src目录)下的类
             Class<?> second = Proxy.class.getClassLoader().loadClass("com.cn.$Proxy");
             System.out.println(second.getSimpleName());
             */
            return o;
        }
    }

    JavaCompiler 是用于编译生成的java代码。在用URLClassLoader将class文件加载进内存。在实例化。

    下面一个测试类 Client

    package com.cn;
    public class Client {
        
        public static void main(String[] args) throws Exception {
            Tree tree = new Tree();
            InvactionHandle handle = new HeightInvactionHandle(tree);
            GrowAble gro = (GrowAble)Proxy.getNewInstance(GrowAble.class, handle);
            gro.growUp();
            System.out.println("测试结束");
        }
    }

    运行结果

    ***************     create java file over      ******************
    ***************     complier class file over      ******************
    ***************     loader class file over      ******************
    I am a tree , I'm grow up!
    这棵树长了2174米!!!
    测试结束

    现在我们在用JDK来做做

    package com.cn;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Random;
    
    public class JDKInvocationHandle implements InvocationHandler {
    
        private Object o;
        public JDKInvocationHandle(Object o)
        {
            super();
            this.o = o;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Object result =  null;
            try {
                result = method.invoke(o,args);
                System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    测试类

    package com.cn;
    public class Client2 {
        
        public static void main(String[] args) {
            java.lang.reflect.InvocationHandler h = new JDKInvocationHandle(new Tree());
            GrowAble gro = (GrowAble) java.lang.reflect.Proxy.newProxyInstance(
                    GrowAble.class.getClassLoader(), 
                    new Class[] { GrowAble.class }, 
                    h);
            gro.growUp();
            System.out.println("测试结束");
        }
    }

    运行结果

    I am a tree , I'm grow up!
    这棵树长了726米!!!
    测试结束

    文章目标 学习java动态代理原理。仅当抛砖引玉作用。

    文章原创。转载请注明http://www.cnblogs.com/stay-9527/p/3689266.html

    Stay Hungry,Stay Foolish!
  • 相关阅读:
    缓存穿透、缓存雪崩、缓存击穿的区别和解决方案
    图解“红黑树”原理,一看就明白!
    Linux系统中常见文件系统格式
    Maven 加载ojdbc14.jar报错,解决方法
    mybatis中#{}和${}的区别
    SqlServer 分页批按时间排序
    Centos7安装与配置domain模式wildfly(默认配置)
    通过java调用Http接口上传图片到服务器
    Spring boot 配置array,list,map
    idea+springboot+freemarker热部署
  • 原文地址:https://www.cnblogs.com/stay-9527/p/3689266.html
Copyright © 2011-2022 走看看