如何优雅的实现一个Mybatis插件

定位:此篇尝试用另一种角度描述如何完成一个mybatis插件,全程可以按段落跳跃阅读,有任何不适欢迎指出Thanks♪(・ω・)ノ

为什么这么设计

如果我想实现一个orm增强插件,首先就应该避免硬编码,最好与业务代码无关。那么就要看看orm框架有没有什么拓展点了。

为了易用,配置越简单越好,要么引入外部配置中心,或者自定义ThreadLocal读取配置。

符合上面这些的,不就是PageHelper吗?

那么自己实现也就方便了,参考PageHelper的思路就可以了

如何增强

这就不得不讲到责任链了,mybatis把所有插件插入到 org.apache.ibatis.session.Configuration#interceptorChain中,等到时候得时候,就返回责任链, 想怎么增强,自己在interceptor中实现即可。

简单配置

第三方配置就不多说了,自己实现即可。

这里参考下PageHelper是通过ThreadLocal,判断上下文参数来判断是否可以跳过。

简单实现一个

gitee仓库地址:gitee.com/Nortyr/alli...

  1. Configuration在初始化的时候创建添加到org.apache.ibatis.session.Configuration#interceptorChain
java 复制代码
@Configuration
@DependsOn("com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration")
public class SelfConfiguration  {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    public void addPageInterceptor() {
        SelfInteceptor interceptor=new SelfInteceptor();
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
        }
    }
}
  1. 这段代码完全可以不这么复杂你可以理解为除了Object obj=invocation.proceed();都是业务增强代码,随便你怎么实现。我再这里主要完成了2个功能
  • 通过方法名+_SELF走一个自己的查询(完全没用,写着玩)
  • 记录下总共pageHelper和sql执行的总共时间(完全没用,写着玩)

完全没有任何实质性的作用,就是写着玩,你们爱怎么实现怎么实现

java 复制代码
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class SelfInteceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start=System.currentTimeMillis();
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        RowBounds rowBounds = (RowBounds) args[2];

        String msId = ms.getId();
        Configuration configuration = ms.getConfiguration();
        Page page = PageHelper.getLocalPage();
        PageHelper.clearPage();

        String countMsId = msId + "_SELF";
        MappedStatement selfMs=configuration.getMappedStatement(countMsId, false);
        Object countResultList = executor.query(selfMs, parameter,rowBounds, resultHandler);
        System.out.println(countResultList);
        Class clazz= PageMethod.class;
        Method m1 = clazz.getDeclaredMethod("setLocalPage",Page.class);
        m1.setAccessible(true);
        m1.invoke(null,page);
        //注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次
        Object obj=invocation.proceed();
        long end=System.currentTimeMillis();
        System.out.println("恭喜你成功浪费了5分钟。。。。"+(end-start));
        return obj;
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

总结

虽然写的很少,但是,PageHelper的底层逻辑应该明白了,就是上面精细化实现没说了,这个应该有别的dalao写过文章了吧,我就懒得写了。再回顾下开篇的2个问题

  • 如何增强
    • 自定义插件加入到interceptorChain中即可,mybatis自己回生成责任链
  • 如何易用
    • PageHelper是通过ThreadLocal实现

gitee地址:gitee.com/Nortyr/alli...

相关推荐
怕浪猫1 分钟前
第21章:微服务与分布式架构中的Go应用
后端·go·编程语言
武子康13 分钟前
大数据-239 离线数仓 - 广告业务实战:Flume 导入日志到 HDFS,并完成 Hive ODS/DWD 分层加载
大数据·后端·apache hive
摸鱼的春哥43 分钟前
Agent教程15:认识LangChain(中),状态机思维
前端·javascript·后端
风象南7 小时前
我把大脑开源给了AI
人工智能·后端
橙序员小站12 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
怒放吧德德12 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆14 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
开心就好202515 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
悟空码字15 小时前
告别“屎山代码”:AI 代码整洁器让老项目重获新生
后端·aigc·ai编程
小码哥_常15 小时前
大厂不宠@Transactional,背后藏着啥秘密?
后端