场景
MyBatis 自定义拦截器,可以拦截的接口只有四种 Executor.class,StatementHandler.class,ParameterHandler.class 和 ResultSetHandler.class。
在某种情景下,如果这四种接口自带的某个方法不能满足我们的要求时,如 Executor主要是创建Statement对象,在创建过程中依靠 MappedStatement 对象将赋值内容给 sql 占位符进行绑定,在Mybatis 中主要有4种实现:BatchExecutor、ReuseExecutor、SimpleExecutor 和 CachingExecutor。如果你觉得这4种实现Executor 接口的 query 方法都不能满足你的要求,那么可以在不改动源码的情况下,建立一个自己的拦截器用于拦截 Executor 接口的 query 方法。
我们可以选择在这些被拦截的方法执行前后加上自己的一些逻辑,也可以选择不执行被拦截的方法,只执行自己的一些逻辑
定义自己的拦截器可以通过实现Mybatis提供的Interceptor接口来实现:
QueryInterceptor.java
package com.lks.plugin;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
/**
* Created by likaisong on 2019/2/25.
*/
@Intercepts(
@Signature(method = "query" ,
type = Executor.class,
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
)
public class QueryInterceptor implements Interceptor {
/**
* 拦截时执行的操作
*
* @param invocation { 代理对象,被监控方法对象,当前被监控方法运行时需要的实参 }
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("被拦截方法执行前的操作。。。");
Object proceed = invocation.proceed();
System.out.println("被拦截方法执行后的操作。。。");
return proceed;
}
/**
* 拦截器用于封装目标对象的
* 通过该方法我们可以返回目标对象本身,也可以返回一个它的代理
*
* @param o
* @return
*/
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
/**
* 用于在 Mybatis 配置文件中指定一些属性的。
*
* @param properties
*/
@Override
public void setProperties(Properties properties) {
}
}
主要实现 plugin 方法和 intercept 方法:
plugin 方法主要是返回拦截的对象,其中Plugin类中有个静态方法 wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。
intercept 方法主要是进行拦截的时候要执行的方法,也就是在此处实现自己的一些操作。
另外,要想MyBatis框架识别出你自定义的拦截器,需要通过注解的方式表明,一个是 @Intercepts,其值是一个@Signature 数组。@Intercepts 用于表明当前的对象是一个 Interceptor,而 @Signature则表明要拦截的接口、方法以及对应的参数类型。
Signature里的参数不清楚的,可以打开你要拦截的方法的源码,如本例子中的query方法:
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
在 MyBatis 配置文件中注册自定义拦截器
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeHandlers>
<typeHandler handler="com.lks.handler.DateToStringTypeHandler" javaType="java.util.Date" jdbcType = "VARCHAR"/>
</typeHandlers>
<plugins>
<plugin interceptor="com.lks.plugin.QueryInterceptor"></plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
主要看plugin节点
userMapper.xml文件中添加测试语句
userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的-->
<mapper namespace="userMapper">
<!--自定义拦截器-->
<select id="queryInterceptor" parameterType="int"
resultType="com.lks.domain.User" >
select * from users where id=#{id}
</select>
</mapper>
测试类中添加测试代码:
TestMain.java
package com.lks.test;
import com.lks.domain.User;
import com.lks.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.testng.annotations.AfterTest;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
/**
* Created by likaisong on 2019/2/24.
*/
public class TestMain {
private static SqlSession session;
private static SqlSession getSqlSession(String resource) {
if (resource == null || "".equals(resource)) {
return null;
}
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
return session;
}
@Test
public static void testQueryIntercepor() {
String resource = "mybatis-config.xml";
session = getSqlSession(resource);
String statement = "userMapper.queryInterceptor";
User user = session.selectOne(statement, 1);
System.out.println(user);
}
@AfterTest
public static void closeSession() {
session.close();
}
}
执行完毕后就可以在控制台中看到打印的语句了,可以根据业务需要,添加自己的逻辑。