IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 从JDK动态代理一步步推导到MyBatis Plugin插件实现原理 -> 正文阅读

[Java知识库]从JDK动态代理一步步推导到MyBatis Plugin插件实现原理

一、前言

最近项目上,要做一个日志审计的功能(通用SDK,期望可以在任何项目中使用),需要对Mybatis层所有的DDL操作做拦截,以将微服务的链路ID、执行人、Controller门面方法全部持久化到业务库。借此机会深入研究了一下MyBatis的源码,该篇文章重点在于推导从JDK动态代理如何一步步演变到MyBatis Plugin的实现。

二、JDK动态代理

0> 概述

JDK动态代理:在程序运行时,利用反射机制获取被代理类的字节码内容 进而生成一个实现代理接口的匿名类(代理类),在调用具体方法前调用InvokeHandler来处理;

  1. JDK Proxy动态代理的API包在java.lang.reflect下,其核心api包含InvocationHandler(Interface)和Proxy(class)。

  2. Proxy是Java动态代理机制的主类,其提供一组静态方法来为一组接口动态地生成代理类及其对象。

    • static InvocationHandler getInvocationHandler(Object proxy) --> 用于获取指定代理对象所关联的调用处理器。

    • static Class getProxyClass(ClassLoader loader, Class[] interfaces) --> 用于获取关联于指定类装载器和一组接口的动态代理类的类对象。

    • static boolean isProxyClass(Class cl) --> 用于判断指定类对象是否具有一个动态代理类

    • static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) --> 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例。

      h:表示当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

  • InvocationHandler是负责连接代理类和委托类的中间类必须要实现的接口。

    • 它定义了一个invoke()方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

1)优点?

  • 可以方便的对地代理类的函数进行统一的处理,而不用修改这个代理类的函数。
  • 简化了编程工作,提高了软件系统的扩展性。因为Java反射机制可以生成任意类型的动态代理类。
  • 动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
  • 接口增加一个方法,动态代理类会直接自动成成对应的代理方法。

2)缺点?

  • JDK Proxy只能对实现了接口的类才能代理,没有接口实现的类,JDK Proxy是无法代理的。

3)实现方式?

  • 1 委托类:JDK动态代理中接口是必须的,要求委托类必须实现某个接口。

  • 2 使用动态代理时,需要定义一个位于代理类与委托类之间的中介类。

    这个中介类必须实现InvocationHandler接口;作为调用处理器“拦截”对代理类方法的调动。

    1. InvocationHandler接口定义如下:

      /**
      调用处理器
      */
      public interface InvocationHandler { 
       
      Object invoke(Object proxy, Method method, Object[] args); 
       
      } 
      

      1> 当我们调用代理类对象的方法时,这个“调用”会转送到invoke()方法中,代理类对象作为proxy参数传入;

      2> 参数method标识了我们具体调用的是代理类的哪个方法;

      3> args为这个方法的参数。

    2. 中介类:

      /**
       * 中介类
       * @author Saint
       */
      public class MyDynamicProxy implements InvocationHandler {
      
          /**
           * obj为委托类对象
           */
          private Object obj;
          
          public MyDynamicProxy(Object obj){
              this.obj = obj;
          }
         
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("before");
              
              Object result = method.invoke(obj, args);
              
              System.out.println("after");
              
              return result;
          }
      }
      

      1> 我们对代理类中的所有方法的调动都会变为对invoke()的调用,我们可以在invoke()方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

      2> 中介类通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。

  • 3 代理类通过Proxy.newProxyInstance()方法生成;

总结一下:中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;代理类与中介类也构成一个静态代理关系。

4)原理?

动态代理关系由两组静态代理关系组成,这就是动态代理的原理;

  • 实际上,中间类(Proxy)与委托类构成了静态代理关系,在这个关系中,中间类是代理类,委托类是委托类。
  • 然后代理类与中间类也构成一个静态代理关系,在这个关系中,中间类是委托类,代理类是代理类。
  • 通过newProxyInstance()方法获取到代理类实例,然后通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke()方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

1> 单纯的动态代理

1)委托类(需要被代理的类)Target

1> Target接口:

/**
 * 委托类(被代理类)接口
 *
 * @author Saint
 */
public interface Target {
    String execute(String name);
}

2> Target实现(具体的委托类):

/**
 * 委托类(被代理类)实现
 *
 * @author Saint
 */
public class TargetImpl implements Target {
    @Override
    public String execute(String name) {
        System.out.println("impl execute()" + name);
        return name;
    }
}

2)中介(中间)类TargetProxy

/**
 * JDK动态代理:中介(中间)类
 *
 * @author Saint
 */
public class TargetProxy implements InvocationHandler {

    /**
     * target为委托类对象
     */
    private Object target;

    public TargetProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before intercept.......");
        Object result = method.invoke(target, args);
        System.out.println("after intercept.......");
        return result;
    }

    /**
     * 创建代理对象
     *
     * @param target 委托类
     * @return
     */
    public static Object wrap(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new TargetProxy(target));
    }
}

这里的wrap(Object target)方法用于创建代理对象。

3)测试类(运行结果)

public class MainTest {
    public static void main(String[] args) {
        // 委托类
        Target target = new TargetImpl();
        // 代理类
        Target targetProxy = (Target) TargetProxy.wrap(target);
        // 实际调用的是代理对象的invoke()方法
        targetProxy.execute("Hello World!");
    }
}

运行结果:

before intercept.......
impl execute()Hello World!
after intercept.......

结果分析:

  • 使用JDK动态代理可以解决问题,实现前置、后置拦截。不过我们要拦截处理的逻辑全部耦合在invoke()方法中,不符合面向对象的思想;想成为一个架构师,必须要抽象起来呀;
  • 尝试设计一个Interceptor接口,需要做什么拦截处理实现接口即可。

2> 抽象出拦截逻辑(增加Interceptor接口)

1)拦截处理逻辑(Interceptor)

Interceptor接口:

/**
 * 抽象拦截器
 *
 * @author Saint
 */
public interface Interceptor {

    /**
     * 具体的拦截处理
     */
    void intercept();
}

Interceptor实现:

  • 日志拦截器
    public class LogInterceptor implements Interceptor {
        @Override
        public void intercept() {
            System.out.println("start record log....");
        }
    }
    
  • 事务拦截器
    public class TransactionInterceptor implements Interceptor {
        @Override
        public void intercept() {
            System.out.println("start transaction...");
        }
    }
    

2)重写代理类TargetProxy2

增加拦截器处理逻辑;在创建代理类TargetProxy2时添加所有的拦截器;

public class TargetProxy2 implements InvocationHandler {

    /**
     * target为委托类对象
     */
    private Object target;

    /**
     * 拦截器集合
     */
    private List<Interceptor> interceptorList = new ArrayList<>();

    public TargetProxy2(Object target, List<Interceptor> interceptorList) {
        this.target = target;
        this.interceptorList = interceptorList;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 拦截处理
         */
        interceptorList.forEach(Interceptor::intercept);

        Object result = method.invoke(target, args);
        return result;
    }

    /**
     * 创建代理对象
     *
     * @param target 委托类
     * @return
     */
    public static Object wrap(Object target, List<Interceptor> interceptorList) {
        TargetProxy2 targetProxy = new TargetProxy2(target, interceptorList);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), targetProxy);
    }
}

3)测试类(运行结果)

public class MainTest2 {
    public static void main(String[] args) {
        // 委托类
        Target target = new TargetImpl();
        // 所有的拦截器
        List<Interceptor> interceptorList = new ArrayList<>();
        interceptorList.add(new LogInterceptor());
        interceptorList.add(new TransactionInterceptor());
        // 代理类
        Target targetProxy = (Target) TargetProxy2.wrap(target, interceptorList);
        // 实际调用的是代理对象的invoke()方法
        targetProxy.execute("Hello World!");
    }
}

运行结果:

start record log....
start transaction...
impl execute()Hello World!

结果分析:

  • 现在可以实现动态添加拦截器了,也不需要每次都去修改中间类TargetProxy了;
  • 不过我们这里只有前置拦截耶,聪明的你肯定发现了,我可以定义两个在Interceptor接口里定义两个方法啊,一个beforeIntercept()、一个afterIntercept();
  • 但是这样拦截器无法知道拦截对象的信息;我们需要做进一步的抽象,将拦截对象的信息封装起来,作为拦截器拦截方法的入参;目标对象的真正执行交给拦截器interceptor来完成,这样我们不仅可以实现前后置拦截,还能对拦截对象的参数做一些修改。参考MyBatis源码,我们设计一个Invocation对象。

3> 抽象逻辑器拦截方法入参

1)拦截方法入参:Invocation

/**
 * 拦截器拦截方法入参
 *
 * @author Saint
 */
public class Invocation {

    /**
     * 目标对象(委托类)
     */
    private Object target;

    /**
     * 执行的目标方法
     */
    private Method method;

    /**
     * 执行方法的参数
     */
    private Object[] args;

    public Invocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    /**
     * 执行目标对象的方法
     *
     * @return
     * @throws Exception
     */
    public Object process() throws Exception {
        return method.invoke(target, args);
    }
}

2)修改拦截接口(Interceptor2)

public interface Interceptor2 {

    /**
     * 具体的拦截处理
     */
    Object intercept(Invocation invocation) throws Exception;
}

事务拦截器实现:

public class TransactionInterceptor2 implements Interceptor2 {
    @Override
    public Object intercept(Invocation invocation) throws Exception {
        System.out.println("start transaction...");
        Object result = invocation.process();
        System.out.println("end transaction...");
        return result;
    }
}

3)中介(中间)类TargetProxy3

public class TargetProxy3 implements InvocationHandler {

    /**
     * target为委托类对象
     */
    private Object target;

    /**
     * 拦截器集合
     */
    private Interceptor2 interceptor2;

    public TargetProxy3(Object target, Interceptor2 interceptor2) {
        this.target = target;
        this.interceptor2 = interceptor2;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Invocation invocation = new Invocation(target, method, args);
        return interceptor2.intercept(invocation);
    }

    /**
     * 创建代理对象
     *
     * @param target 委托类
     * @return
     */
    public static Object wrap(Object target, Interceptor2 interceptor2) {
        TargetProxy3 targetProxy = new TargetProxy3(target, interceptor2);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), targetProxy);
    }
}

4)测试类(运行结果)

public class MainTest3 {
    public static void main(String[] args) {
        // 委托类
        Target target = new TargetImpl();
        // 所有的拦截器
        Interceptor2 transactionInterceptor = new TransactionInterceptor2();
        // 代理类
        Target targetProxy = (Target) TargetProxy3.wrap(target, transactionInterceptor);
        // 实际调用的是代理对象的invoke()方法
        targetProxy.execute("Hello World!");
    }
}

运行结果:

start transaction...
impl execute()Hello World!
end transaction...

结果分析:

  • 此时拦截器已经可以获取到拦截对象的信息;不过这样仿佛又回到了最初的起点,只是将原本写在代理类TargetProxy中的逻辑移到了Interceptor,并且此时拦截器也只有一个;
  • 上面这种做法看着就比较别扭,我们换种方式理解:对于目标类(委托类)而言,它只需要知道它内部被插入了哪些拦截器;
  • 换言之,我们可以把拦截器看做是委托类和代理类的中间类(TargetProxy)的代理类,即中间类执行方法的动作由拦截器负责完成;
  • 针对多个拦截器而言,也可以兼容,实际就是代理嵌套再代理。

4> 拦截器里插入目标对象

1)重写拦截器

public interface Interceptor3 {

    /**
     * 具体的拦截处理
     */
    Object intercept(Invocation invocation) throws Exception;

    /**
     * 插入目标类(即拦截器作用于那个委托类)
     */
    Object plugin(Object target);
}

事务拦截器:

public class TransactionInterceptor3 implements Interceptor3 {
    @Override
    public Object intercept(Invocation invocation) throws Exception {
        System.out.println("start transaction...");
        Object result = invocation.process();
        System.out.println("end transaction...");
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return TargetProxy4.wrap(target, this);
    }
}

日志拦截器:

public class LogInterceptor2 implements Interceptor3 {
    @Override
    public Object intercept(Invocation invocation) throws Exception {
        System.out.println("start record log...");
        Object result = invocation.process();
        System.out.println("end record log...");
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return TargetProxy4.wrap(target, this);
    }
}

2)中间类(TargetProxy4)和3>一样

public class TargetProxy4 implements InvocationHandler {

    /**
     * target为委托类对象
     */
    private Object target;

    /**
     * 拦截器集合
     */
    private Interceptor3 interceptor3;

    public TargetProxy4(Object target, Interceptor3 interceptor3) {
        this.target = target;
        this.interceptor3 = interceptor3;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Invocation invocation = new Invocation(target, method, args);
        return interceptor3.intercept(invocation);
    }

    /**
     * 创建代理对象
     *
     * @param target 委托类
     * @return
     */
    public static Object wrap(Object target, Interceptor3 interceptor3) {
        TargetProxy4 targetProxy = new TargetProxy4(target, interceptor3);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), targetProxy);
    }
}

3)测试类(执行结果)

public class MainTest4 {
    public static void main(String[] args) {
        // 委托类
        Target target = new TargetImpl();
        // 所有的拦截器
        Interceptor3 transactionInterceptor = new TransactionInterceptor3();
        Interceptor3 logInterceptor = new LogInterceptor2();
        // 由于log拦截器在最后,所以log拦截器先执行
        target = (Target) transactionInterceptor.plugin(target);
        target = (Target) logInterceptor.plugin(target);

        // 实际调用的是代理对象的invoke()方法
        target.execute("Hello World!");
    }
}

执行结果:

start record log...
start transaction...
impl execute()Hello World!
start transaction...
end record log...

结果分析:

  • 最后插入的拦截器先执行;
  • 本质上来讲这就是代理嵌套再代理;

时序图如下:
请添加图片描述
哈哈,感觉这样就很好了撒;不过添加拦截器的方式我们可以再优化一下,我们再利用一下OOP的思想,搞个拦截器链;

5> 拦截器链

紧接着上一种方式,我们再利用责任链模式,将拦截器串起来,搞成一个链;

1)拦截器链(InterceptorChain)

public class InterceptorChain {
    private List<Interceptor3> interceptorList = new ArrayList<>();

    /**
     * 插入所有拦截器
     *
     * @param target
     * @return
     */
    public Object pluginAll(Object target) {
        for (Interceptor3 interceptor : interceptorList) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor3 interceptor) {
        interceptorList.add(interceptor);
    }

    /**
     * 返回一个不可修改集合,只能通过addInterceptor方法添加
     *
     * @return
     */
    public List<Interceptor3> getInterceptorList() {
        return Collections.unmodifiableList(interceptorList);
    }
}

这里通过pluginAll()方法包一层,将所有的拦截器插入到目标类中去;

2)测试类(执行结果)

public class MainTest5 {
    public static void main(String[] args) {
        // 委托类
        Target target = new TargetImpl();
        // 所有的拦截器
        Interceptor3 transactionInterceptor = new TransactionInterceptor3();
        Interceptor3 logInterceptor = new LogInterceptor2();
        InterceptorChain interceptorChain = new InterceptorChain();
        interceptorChain.addInterceptor(transactionInterceptor);
        interceptorChain.addInterceptor(logInterceptor);
        // 由于log拦截器在最后,所以log拦截器先执行
        target = (Target) interceptorChain.pluginAll(target);
        // 实际调用的是代理对象的invoke()方法
        target.execute("Hello World!");
    }
}

执行结果:

start record log...
start transaction...
impl execute()Hello World!
end transaction...
end record log...

结果分析:

  • 不分析了,到这也就结束了。

三、MyBatis Plugin插件

在MyBatis Plugin源码中我们可以看到,基本和我们上面推导的最后一种方式一样,其中Plugin相当于我们的TargetProxy。
在这里插入图片描述
对于MyBatis插件的源码分析,且听下文分解。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:35:37  更:2022-03-21 20:37:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:51:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码