• jdk动态代理使用及原理


    jdk动态代理的使用

    1.创建实现InvocationHandler接口的类,实现invoke(Object proxy, Method method, Object[] args)接口,其中invoke()执行的方法就为代理实例对象执行的方法。

    其中proxy为代理对象,method为方法,args为方法的参数。

    要想在原类方法上在进行再处理(如记录日志等),需要用构造方法把接口子类的实例传入,用method.invoke(原对象,args),则执行invoke里的函数原函数。

    2.创建代理实例对象

       public static <T> T getProxy(Class<T> inf){
            return (T) Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new ProxyInvocationHandler());
       }

    其中inf为借口,第一个参数为借口的类加载器,第二个参数为接口的Class数组,第三个为代理的执行体。

    最后根据返回的接口代理实例对象执行相应的方法即可。

    /**
     * 被代理接口
     */
    public interface Myinterface {
        public List<Object> queryList();
        public String getName();
    }
    
    /**
     * 代理方法,Mybatis中接口无实例类,
     * 所以此处理并不是在方法执行前后加日志等处理
     * 而是生成数据库操作执行体
     */
    public class MapperProxy implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //当方法名为queryList时
            if (method.getName().equals("queryList")) {
                List list = new ArrayList();
                list.add("方法名为queryList()的代理执行结果");
                list.add(2);
                list.add("autumn");
                return list;
            }
            //当放回类型为String时
            if (method.getReturnType().toString().equals(String.class.toString())){
                return "查询数据库返回的字符串";
            }
            return null;
        }
    }
    
    /**
     * 根据接口名称获取代理实例
     */
    public class SqlSession {
        /**
         * 获取接口代理对象实例
         * @param inf 接口
         * @param <T> 该接口的代理对象实例
         * @return
         */
        public static <T> T getMapper(Class<T> inf){
           return (T)Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new MapperProxy());
        }
    }
    
    /**
     * 测试接口无子类代理对象
     */
    public class Demo {
        public static void main(String[] args) {
            Myinterface inf = SqlSession.getMapper(Myinterface.class);
            List<Object> result = inf.queryList();
            System.out.println("返回结果:"+result);
            System.out.println("----------------------------------------");
            System.out.println("返回结果:"+inf.getName());
            System.out.println("----------------------------------------");
        }
    }

    还有一种用法是传递接口的子类实例对象,在子类的实例对象方法执行前后加上其他操作(如日志处理等),传送门

    jdk动态代理原理

    jdk动态代理主要是一个构造函数newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)的原理

    /**
     * 车类接口
     */
    public interface Moveable {
        public void move();
    }
    
    /**
     * 车类接口具体实现类
     */
    public class Car implements Moveable {
        
        @Override
        public void move() {
            try {
                Thread.sleep(new Random().nextInt(3*1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("car is running...");
        }
    }
    
    /**
     * 模拟jdk中的InvocationHandler接口
     */
    public interface InvocationHandler {
    
        public void invoke(Object o, Method m);
    }
    
    /**
     * 代理实例的处理方法
     * 在原本的实例基础上进行增加功能
     * 接口也可以无子类,直接通过接口方法名调用代理的invoke方法
     */
    public class TimeHandler implements InvocationHandler {
    
        private Object target;
        
        public TimeHandler(Object target) {
            super();
            this.target = target;
        }
    
        @Override
        public void invoke(Object o, Method m) {
            try {
                long starttime = System.currentTimeMillis();
                System.out.println("开始计时....");
                m.invoke(target);
                long endtime = System.currentTimeMillis();
                System.out.println("计时结束,用时"
                                + (endtime - starttime) + "毫秒");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
    public class Proxy {
    
        /**
         * 根据接口动态生成接口的子类对象并实现方法体,执行方法为h的invoke
         * @param infce 被代理接口
         * @param h  被代理类的方法执行体
         * @return
         * @throws Exception
         */
        @SuppressWarnings("unchecked")
        public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{
            String rt = "
    ";
            String methodStr = "";
            for(Method m : infce.getMethods()){
                methodStr += "    @Override" + rt +
                        "    public void " + m.getName() + "() {" + rt +
                        "  try{" + rt +
                        "  Method md = " + infce.getName() + ".class.getMethod(""
                        + m.getName() + "");" + rt +
                        "  h.invoke(this,md);" +rt+
                        "  }catch(Exception e){ e.printStackTrace();}" + rt +
                        "    }" ;
            }
    
            String str =
                    "package com.qy.dymanic.jdkproxycode;" + rt +
                            "import java.lang.reflect.Method;" + rt +
                            "import com.qy.dymanic.jdkproxycode.InvocationHandler;" +  rt+
                            "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                            "    public $Proxy0(InvocationHandler h) {" + rt +
                            "        this.h = h;" + rt +
                            "    }" + rt +
                            "  private InvocationHandler h;" + rt+
                            methodStr + rt +
                            "}" ;
            //产生代理类的java文件
            String filename = System.getProperty("user.dir") +"/bin/com/qy/dymanic/jdkproxycode/$Proxy0.java";
            File file = new File(filename);
            FileUtils.writeStringToFile(file, str);
    
            //编译
            //拿到编译器
            JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
            //文件管理者
            StandardJavaFileManager fileMgr =
                    complier.getStandardFileManager(null, null, null);
            //获取文件
            Iterable units = fileMgr.getJavaFileObjects(filename);
            //编译任务
            CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
            //进行编译
            t.call();
            fileMgr.close();
    
            //load 到内存
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Class c = cl.loadClass("com.qy.dymanic.jdkproxycode.$Proxy0");
    
            Constructor ctr = c.getConstructor(InvocationHandler.class);
            return ctr.newInstance(h);
        }
    
    }
    
        /**
         * 模拟jdk动态代理源码
         * @throws Exception 
         */
        public static void main(String[] args) throws Exception {
            Car car = new Car();
            InvocationHandler h = new TimeHandler(car);
            Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
            m.move();
        }

    会在bin目录下生成两个文件

    其中生成的$Proxy0.java代码如下,此类为动态生成的接口的代理实例类。

    package com.qy.dymanic.jdkproxycode;
    import java.lang.reflect.Method;
    import com.qy.dymanic.jdkproxycode.InvocationHandler;
    public class $Proxy0 implements com.qy.dymanic.jdkproxycode.Moveable {
        
        public $Proxy0(InvocationHandler h) {
            this.h = h;
        }
        
        private InvocationHandler h;
        
        @Override
        public void move() {
        try{
          Method md = com.qy.dymanic.jdkproxycode.Moveable.class.getMethod("move");
          h.invoke(this,md);    //这个是重点!!!InvocationHandler调用它的invoke(代理类,方法名)
       }catch(Exception e){ e.printStackTrace();} } }

    源码

  • 相关阅读:
    file_get_contents高級用法
    php缓存技术常用函数
    php爬虫的两种思路
    jQuery判断文本框是否为空
    Asp.net 实现图片缩放 无水印(方法二)
    C# winform关于DataGridView的一些操作
    PHP判断访问者手机移动端还是PC端的函数
    本地图片显示在网页
    如何安装 Composer
    国内开源镜像站
  • 原文地址:https://www.cnblogs.com/aeolian/p/8434903.html
Copyright © 2020-2023  润新知