MyBatis的自定义插件

这篇具有很好参考价值的文章主要介绍了MyBatis的自定义插件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

MyBatis的自定义插件

前置知识

MyBatis 可以拦截的四大组件

  • Executor - 执行器
  • StatementHandler - SQL 语句构造器
  • ParameterHandler - 参数处理器
  • ResultSetHandler - 结果集处理器

自定义 MyBatis 插件

/**
 * 打印 sql 执行的时间插件
 */
@Intercepts(
	// 指定拦截器拦截的对象、方法和参数类型
    {@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
    @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})}
)
// 注册到 Spring 容器,不是 Spring 环境的话可以用 mybatis 的 config 配置进去
@Component
public class SqlExecuteTimePrintMybatisPlugin implements Interceptor {

    protected Logger logger = LoggerFactory.getLogger(SqlExecuteTimePrintMybatisPlugin.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取代理对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 获取执行 sql
        BoundSql boundSql = statementHandler.getBoundSql();
        // 此处简单处理一下,只打印参数替换前的 sql,目的是演示自定义插件
        String sql = boundSql.getSql();
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            logger.info("sql -> {}, takes time -> {}", sql, System.currentTimeMillis() - start);
        }
    }
}

效果如下

2023-10-14 17:18:39.297  INFO 25972 --- [p-nio-80-exec-1] c.y.m.c.SqlExecuteTimePrintMybatisPlugin : sql -> SELECT * FROM `INFO` WHERE `id` = ? , takes time -> 57
2023-10-14 17:18:39.324  INFO 25972 --- [p-nio-80-exec-1] c.y.m.c.SqlExecuteTimePrintMybatisPlugin : sql -> SELECT `id`, `info_id`, `extend_info` FROM `INFO_DETAIL` WHERE `info_id` = ?, takes time -> 4

源码解析

创建四大对象的代码如下

public class Configuration {
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 此处增加拦截器责任链
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }

    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        // 此处增加拦截器责任链
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        // 此处增加拦截器责任链
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

    public Executor newExecutor(Transaction transaction) {
    	return newExecutor(transaction, defaultExecutorType);
    }

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
        	executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
        	executor = new ReuseExecutor(this, transaction);
        } else {
        	executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
        	executor = new CachingExecutor(executor);
        }
        // 此处增加拦截器责任链
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }
}
  1. 首先在创建 Executor、StatementHandler、ParameterHandler、ResultSetHandler 四个对象时,将插件(plugins)注入

  2. 调用 InterceptorChain.pluginAll() 方法将插件增加到责任链,并返回代理后的 target 包装对象,InterceptorChain 保存了所有的拦截器(Interceptors)

  3. 最终在执行的时候调用的其实是 JDK 动态代理的对象,执行 MyBatis 中 InvocationHandler 的实现 org.apache.ibatis.plugin.Plugin 的 invoke 方法

public class InterceptorChain {

    private final List<Interceptor> interceptors = new ArrayList<>();

    public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
     	target = interceptor.plugin(target);
    }
    	return target;
    }
}
public interface Interceptor {
	// 拦截器增强方法
    Object intercept(Invocation invocation) throws Throwable;
	// 包装原来的对象
    default Object plugin(Object target) {
    	return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
    	// NOP
    }

}
public class Plugin implements InvocationHandler {

    private final Object target;
    private final Interceptor interceptor;
    private final Map<Class<?>, Set<Method>> signatureMap;

    private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
    }

    public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
		// jdk 动态代理
        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 {
            // 获取插件中生命要增强的方法
            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);
        }
    }
    ...
}

备注

不明白的需要去看下 JDK 动态代理实现原理,概括的来讲就是 Proxy#newProxyInstance 时,通过字节码增强的方法,生成一个实现了跟被代理类相同接口并继承了 java.lang.reflect.Proxy 的类并返回其实例,调用这个代理类的方法时,实际上调用的是 Proxy.InvocationHandler.invoke(this, method, new Object[]{args}) 方法文章来源地址https://www.toymoban.com/news/detail-725281.html

到了这里,关于MyBatis的自定义插件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MyBatis——MyBatis插件原理

    本博文主要介绍MyBatis插件机原理,帮助大家更好的理解和学习MyBatis。 MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的

    2024年02月10日
    浏览(31)
  • MyBatis第八讲:MyBatis插件机制详解与实战

    MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能。那么拦截器拦截MyBatis中的哪些内容呢?本文是MyBatis第8讲,对MyBatis插件机制详解。

    2024年02月13日
    浏览(44)
  • MyBatis 03 -MyBatis动态SQL与分页插件

    MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。 动态查询 where标签和if标签组合使用 动态修改 1.1 sql sql标签的作用是提取公共的sql代码片段 sql id属性:唯一标识 include refid属性:参照id 动态查询

    2023年04月15日
    浏览(31)
  • IntelliJ IDEA安装Mybatis 插件Free Mybatis plugin

    需求描述 在开发一些Mybatis的项目,经常需要写一个Mapper接口,在找代码过程,经常需要去找对应的xml文件,所以非常的不方便。自从有了免费的free-mybatis-plugin插件之后 ,在可以实现在idea里一键跳转到对应的xml文件,反之,在xml文件也可以一键跳转到对应mapper接口。 也有其

    2024年02月15日
    浏览(98)
  • SpringBoot(整合MyBatis + MyBatis-Plus + MyBatisX插件使用)

    1.需求分析 2.数据库表设计 3.数据库环境配置 1.新建maven项目 2.pom.xml 引入依赖 3.application.yml 配置数据源 数据库名 用户名 密码 驱动是mysql8的(因为上面使用了版本仲裁) 4.Application.java 编写启动类 5.测试 6.配置类切换druid数据源 7.测试数据源是否成功切换 4.Mybatis基础配置 1

    2024年03月20日
    浏览(45)
  • MyBatis分页插件

    分页插件使用。 添加依赖 配置分页插件 在MyBatis的核心配置文件(mybatis-config.xml)中配置插件 开启分页功能 在查询功能之前使用 PageHelper.startPage(int pageNum, int pageSize) 开启分页功能 pageNum:当前页的页码 pageSize:每页显示的条数 分页相关数据 方法一:直接输出 分页相关数据

    2024年02月09日
    浏览(32)
  • 十二、MyBatis分页插件

    1. 添加依赖 2. 配置分页插件 在MyBatis的核心配置文件中配置插件 3. 在查询接口之前写上 在查询功能之前使用 PageHelper.startPage(int pageNum, int pageSize) 开启分页功能 pageNum :当前页的页码 pageSize :每页显示的条数 在查询获取list集合之后,使用PageInfoT pageInfo = new PageInfo(ListT list,

    2024年02月03日
    浏览(27)
  • Mybatis(七):分页插件

    本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远! Mybatis 是一款非常流行的持久层框架,可以帮助我们轻松地实现数据库操作和数据访问。在 Mybatis中

    2024年01月16日
    浏览(25)
  • Mybatis 分页插件 PageHelper

    今天记录下 Mybatis 分页插件 pageHelper 的使用。 有一个员工表(employee),现在要使用 pageHelper 插件实现员工的分页查询。 Employee employee类定义代码如下 EmployeeDTO EmployeeDTO定义代码如下 PageResult 最后返回给前端的数据封装在 PageResult 类中, PageHelper 类定义如下: EmployeePageQueryDTO 分

    2024年01月19日
    浏览(41)
  • Mybatis执行流程、缓存机制、插件

    目录 一.传统的JDBC连接数据库 二.Mybatis(ORM框架) 1.什么是ORM框架 2.Mybatis特性 三.MyBatis缓存机制 1.一级缓存 2.二级缓存 3.一二级缓存很鸡肋 四.Mybayis插件 插件的工作流程 多个插件的顺序 五.Mybatis执行流程 1.初始化解析配置文件 2.创建sqlSession会话 3.获取mapper代理对象 4.调用map

    2024年02月04日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包