2024-12-22  阅读(105)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/4119798390

回答

Mybatis 的运行涉及到 Executor、StatementHandler、ParameterHandler、ResultSetHandler 等组件,插件的工作原理就是在这些组件执行过程中,插入一些自定义的代码逻辑。

MyBatis 插件机制允许用户自定义拦截器(实现Interceptor接口),以在执行 SQL 的各个阶段进行额外的处理。

说一下 Mybatis 的工作原理?

实现机制

Mybatis 使用 JDK 动态代理,为目标对象生成代理对象。从而在目标对象方法调用时被拦截器拦截处理。

public class Plugin implements InvocationHandler {

  /**
  * 创建代理对象
  */
  public static Object wrap(Object target, Interceptor interceptor) {
    // 获取拦截器关注的类和方法签名的映射  
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    // 获取目标对象实现的所有接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    // JDK 动态代理创建目标对象  
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 从签名 Map 获取方法  
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      // 存在则执行拦截器逻辑  
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

}

扩展

Mybatis 插件的实现步骤

  • 定义拦截器:实现Interceptor接口。
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 添加自定义的拦截逻辑
        System.out.println("Before executing SQL");
        Object returnValue = invocation.proceed(); // 继续执行原方法
        System.out.println("After executing SQL");
        return returnValue;
    }

    @Override
    public Object plugin(Object target) {
        // 创建代理对象。
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
    }
}

  • 配置拦截器:在 MyBatis 配置文件中注册拦截器。

方法一

<configuration>
    <plugins>
        <plugin interceptor="com.damingge.interceptor.MyInterceptor"/>
    </plugins>
</configuration>

方法二

@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        // 设置自定义插件
        Interceptor[] plugins = {new MyInterceptor()};
        sessionFactoryBean.setPlugins(plugins);
        return sessionFactoryBean.getObject();
    }
}

备注:在 MyBatis 中,支持多个拦截器。拦截器之间的执行顺序由它们在配置中的声明顺序决定。

  1. 拦截目标方法:在拦截器中定义要拦截的方法和逻辑。
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})

@Intercepts @Signature 注解

  • type:拦截的对象类型。
  • method:拦截的方法名称。
  • args:方法的参数类型。

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文