上一篇:Mybatis,动态代理CRUD源码分析
目录
1. 引言
2. 自定义插件的编写逻辑:根据Mybatis规则 编写一个拦截器,在拦截器内部加入自定义增强功能
2.1 编写拦截器
2.2 拦截器还要配置到mybatis里面,这里使用注解@Intercepts
2.3 在mybatis-config.xml文件中配置
2.4 测试
3. 配置多个拦截器
4. 拦截器的作用
5. 修改参数
5.1 写拦截器
完整MyInterceptor
上一篇中的Mybatis底层的四个处理器:StatementHandler 、ParameterHandler、ResultSetHandler、 TypeHandler
插件也涉及到4个核心对象:StatementHandler 、ParameterHandler、ResultSetHandler、Executor
回顾mybatis动态代理对象进行增删改查操作
mapper.queryStudentById()方法添加断点,调试进入方法内部
调用了invoke()方法,
进入execute()方法
进入selectOne()方法
进入selectList()方法
进入executor.query()方法
进入delegate.query()方法
进入queryFromDatabase()方法
进入doQuery()方法,在该方法可以看到我们的核心对象:StatementHandler
进入newStatementHandler(),把处理器statementHandler给interceptorChain.pluginAll(),该方法增强处理器statementHandler
拦截器类似于如下逻辑:
进入interceptorChain.pluginAll();
可以看到拦截器Interceptor接口在org.apache.ibatis.plugin包下
上诉拦截器,4个核心对象(StatementHandler 、ParameterHandler、ResultSetHandler、Executor)都有
四大核心对象:都涉及到了拦截器 用于增强 ,四大核心对象都包含了 该增强操作
自定义插件编写步骤:
实现刚刚提到的拦截器:Interceptor接口,注意在org.apache.ibatis.plugin接口中:
可以看到interceptor接口有3个方法:intercepted()、plugin()、setProperties()。
1. 先来看intercept()方法的放行方法:invoccation.proceed()
2. 再来看plugin()方法
plugin()方法将拦截器中定义的增强功能和4大核心对象封装成一个整体
3. 最后setProperties()方法
@Intercepts注解中使用{}添加@Signature注解
拦截器可以拦截4大核心对象(StatementHandler、ParameterHandler、ResultSetHandler、Execute)其中一个
通过type属性可以配置
这里我们拦截StatementHandler的query()方法
拦截上面的query(Statement var1,ResultHandler var2)方法
上面代码args的值给错了,第二个参数应该是ResultHandler类型,在org.apache.ibatis.session.ResultHandler包下
完整代码:
package com.lyx.mybatis.interceptors;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class,
method = "query",
args = {Statement.class, ResultHandler.class})
})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("这是一个拦截方法");
Object proceed = invocation.proceed();
System.out.println(proceed);
return proceed;
}
//plugin()方法将拦截器中定义的增强功能和4大核心对象封装成一个整体
@Override
public Object plugin(Object o) {
Object wrap = Plugin.wrap(o, this);
return wrap;
}
//设置属性
@Override
public void setProperties(Properties properties) {
System.out.println("设置属性"+properties);
}
}
黑色方框中为intercept()方法的输出内容,由于我的queryStudentById()方法到数据库里面查了2次数据,查询Student,再通过Student的外键查classroom表,所以intercept()方法执行两次。并打印Object proceed;
刚刚plugin()方法没有打印信息,现在添加上,测试类改为UserTest,输出如下:可以看到plugin()方法执行了4次--->plugin()方法把拦截器和4大核心对象进行封装时会每一个都尝试封装一下
在mybatis-config.xml文件中配置:
分析:
编写多个拦截器时,执行顺序和
配置顺序一致
例:
select * from student ➔ 拦截器 ➔ select * from student limit 3,5(例如分页插件)
select * from student where Id = 1 ➔ 拦截器 ➔ select * from student where Id = 3
为MyInterceptor类的intercept()方法添加如下
Object target = invocation.getTarget();
System.out.println("目标对象"+target);
测试运行:
看到target是RoutingStatementHandler类型
再添加代码:
MetaObject metaObject = SystemMetaObject.forObject(target);
metaObject.getValue("parameterHandler.parameterObject");
metaObject .getValue("xxx") :该方法可以获取RoutingStatementHandler 对象
从而通过RoutingStatementHandler.getBoundSql()获取到XxxMapper.xml中的sql:
metaObject.getValue("parameterHandler.boundsql.sql")
通过RoutingStatementHandler.getParameterHandler()取到XxxMapper.xml中的sql的参数值:
metaObject.getValue("parameterHandler.parameterObject");
我们来获取一下queryUserById(12)方法的参数值:12
执行:
由此,我们可以通过拦截器去修改这个参数值
添加注解
@Intercepts({
@Signature(
type = ,
method = ,
args =
)
})
修改参数代码如下:
invocation.proceed();放行要最后写
执行:
改为11:
测试成功
以上方法通过metaObject.setValue("parameterHandler.parameterObject",2);修改了参数
也可以通过metaObject.setValue("parameterHandler.boundSql.sql","select * from..");
package com.lyx.mybatis.interceptors;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class,
method = "parameterize",
args = Statement.class)
})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("方法的参数值为:"+value);
metaObject.setValue("parameterHandler.parameterObject",11);
Object value2 = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("修改后方法的参数值为:"+value2);
Object proceed = invocation.proceed();
System.out.println(proceed);
return proceed;
}
@Override
public Object plugin(Object o) {
Object wrap = Plugin.wrap(o, this);
System.out.println("这是plugin : " + wrap);
return wrap;
}
@Override
public void setProperties(Properties properties) {
System.out.println("这是设置属性" + properties);
}
}