背景
相信mybatisplus应该很多人都用过,当然有些人在项目中使用的是tk.mybatis,那么今天说到这个话题原理都是一样,首先mybatisplus会有很多插件,这些插件都会形成一个拦截器链路,具体可以学习下原理,这个地方就不过多叙说了,毕竟不是本篇博文的重点,那言归正传,比如哪些插件呢,分页插件必要的,其次mybatis动态修改SQL的语句,各种插件都需要添加到这个拦截链路中也就是plus中
mybatis失效的案例分享
这个是我定义的一个mybatis一个插件,结果是当我执行一个查询的时候并没有进行拦截
那这个问题涉及到mybatis这个插件的底层原理以及spring bean创建过程,还有spring的bean创建顺序的原理
首先我们这个项目用的是springboot+mybatis plus,为了让其实现一个分页的效果,肯定需要添加一个分页插件
@Configuration
@ConditionalOnClass(MybatisPlusInterceptor.class)
public class PagePluginConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
这样就可以实现分页的效果了 除此之外我还配置了一个自定义的一个mybatis插件
比如实现读写分离、一些日志操作、一些对SQL动态修改的功能,但是这个时候我这个自定义的插件没有生效?
那接下来我们一步步来分析下为什么
那么第一步,我们先确认下我们这个自定义插件有没有被spring识别到并且创建bean,但是从代码上看确实加了这个@component这个注解 所以会被扫描到
打了个断点,可以看到第一个就是我们的自定义插件,第二个就是分页插件
其实答案很明显了那就是插件执行的顺序问题了,我们都知道拦截链都是一个圆环,先加入到这个拦截器链的就是最后一个执行的,也就是说在这个地方是分页插件先执行,但是先执行就为什么无法执行后面的插件呢,其实分页插件就是mybatis插件中最后一个执行的,为什么这样说呢,看下源码
分页插件并没有执行invocation.proceed();这个方法,这方法是执行目标方法的,如果不调用这个插件也不会触发下一个插件
invocation.proceed() 方法在 MyBatis 的插件机制中扮演着至关重要的角色。它是一个类似于 Java 动态代理中的 InvocationHandler.invoke() 方法的作用,用于触发拦截器链中的下一个拦截器,或者是最终的目标方法(如果没有其他拦截器了)。
当你在 intercept 方法中调用 invocation.proceed() 时,会继续执行拦截器链中的下一个拦截器,或者如果当前是最后一个拦截器,则会执行目标对象的实际方法。如果某个插件执行完之后没有调用 invocation.proceed(),则不会继续执行后续的拦截器,也不会执行目标方法。这会导致整个调用链路中断,从而可能导致应用程序错误或异常。
具体来说:
1、不调用 invocation.proceed() 的影响:
如果你不调用 invocation.proceed(),那么当前插件之后的所有拦截器都不会被触发。
目标方法也不会被执行,这可能会导致应用程序无法正常完成预期的操作,例如数据库查询或更新不会发生。
应用程序可能会因为缺少必要的处理步骤而失败。
2、调用 invocation.proceed() 的意义:
调用 invocation.proceed() 可以使拦截器链中的下一个插件有机会执行其逻辑。
如果当前插件是最后一个拦截器,那么调用 invocation.proceed() 会使目标方法被执行。
示例
以下是一个简单的示例,展示了如何在 intercept 方法中使用 invocation.proceed():
public class CustomPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("Before method call");
Object result = invocation.proceed(); // 继续执行下一个插件或目标方法
System.out.println("After method call");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 设置插件属性
}
}
在这个例子中,intercept 方法会在执行目标方法之前和之后打印消息。通过调用 invocation.proceed(),确保了目标方法能够被执行。
总结来说,invocation.proceed() 是用来继续执行拦截器链中的下一步骤的关键方法。如果不调用它,将会阻止进一步的执行,从而可能导致应用程序逻辑上的问题。因此,在编写 MyBatis 插件时,一定要注意正确调用 invocation.proceed()。
那分页插件为什么最后没有调用这个方法呢
在 MyBatis 的分页插件设计中,通常会在 intercept 方法中对 SQL 语句进行修改,而不是直接调用 invocation.proceed()。这是因为分页插件的主要目的是修改 SQL 语句,使其包含分页逻辑(例如添加 LIMIT 和 OFFSET 子句),而不是简单地继续执行原生的 SQL 语句。
分页插件的工作原理通常是这样的:
- 拦截 SQL 语句