Mybatis 插件的工作原理?

 作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题


代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等


联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等

回答

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:方法的参数类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值