当前位置: 首页 > news >正文

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 = "\r\n";
        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();} } }

源码

转载于:https://www.cnblogs.com/aeolian/p/8434903.html

相关文章:

  • mariadb/mysql使用Navicat连接报错
  • 【10】万魂杀服务器开发之特性 条件触发器(Condition Trigger)机制
  • 【Lv1-Lesson007】Where Are You From?
  • 写一个H5___type:range__调色板
  • React 组件生命周期
  • STL的注意事项
  • hdu 4857 Little Devil I
  • Spring Boot实践--项目打包、启动、关闭的方法
  • centos7 安装 gitolite (git服务器)
  • 项目去掉svn管理标志
  • SSM-MyBatis-09:Mybatis中SqlSession的close为什么能造成事务的回滚
  • Javascript理解this对象
  • GNUPG
  • 零基础Python爬虫实现(百度贴吧)
  • 我对CopyOnWrite的思考
  • python3.6+scrapy+mysql 爬虫实战
  • Bytom交易说明(账户管理模式)
  • express + mock 让前后台并行开发
  • Java|序列化异常StreamCorruptedException的解决方法
  • jQuery(一)
  • leetcode386. Lexicographical Numbers
  • Solarized Scheme
  • tensorflow学习笔记3——MNIST应用篇
  • XML已死 ?
  • 创建一种深思熟虑的文化
  • 工作手记之html2canvas使用概述
  • 回顾2016
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 坑!为什么View.startAnimation不起作用?
  • 力扣(LeetCode)357
  • 前端
  • 提醒我喝水chrome插件开发指南
  • 消息队列系列二(IOT中消息队列的应用)
  • #### go map 底层结构 ####
  • $.ajax,axios,fetch三种ajax请求的区别
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (Matlab)使用竞争神经网络实现数据聚类
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (搬运以学习)flask 上下文的实现
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (南京观海微电子)——COF介绍
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)Linux+Windows下安装ffmpeg
  • (原創) 物件導向與老子思想 (OO)
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)创业家杂志:UCWEB天使第一步
  • ***详解账号泄露:全球约1亿用户已泄露
  • ..回顾17,展望18
  • .htaccess配置重写url引擎
  • .net core webapi 大文件上传到wwwroot文件夹
  • .net Signalr 使用笔记
  • .net 怎么循环得到数组里的值_关于js数组
  • .net和php怎么连接,php和apache之间如何连接
  • .net下的富文本编辑器FCKeditor的配置方法