MyBatis Interceptor
MyBatis的拦截器可以用于在以下对象及方法中拦截修改:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
每一个方法会对应不同的参数列表, 这些需要体现在Intercepts的Signature中
配置
除了实现Interceptor接口以外, 需要在项目中做以下配置
application.yml
增加对应的mybatis-config.xml, 因为现在还不支持在yml中配置plugin
mybatis: type-aliases-package: com.compayn.proj.commons.api.dto mapper-locations: classpath:mapper/*.xml config-location: classpath:mybatis-config.xml
mybatis-config.xml
增加mybatis配置文件, 注意这里配置的拦截器, 其实际执行顺序是自下而上的
<?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> <settings> <setting name="..." value="true" /> </settings> <plugins> <!-- The invocation order is bottom up --> <plugin interceptor="com.company.proj.commons.impl.interceptor.BaseInterceptor" /> </plugins> </configuration>
Interceptor实现
一个简单的拦截器实现, 可以输出执行的sql, 以及执行时间.
import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Statement; import java.util.Properties; @Intercepts({ @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class}), @Signature(type = StatementHandler.class, method = "update", args = { Statement.class}), @Signature(type = StatementHandler.class, method = "query", args = { Statement.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class BaseInterceptor implements Interceptor { private static Logger logger = LoggerFactory.getLogger(BaseInterceptor.class); /** * 用于封装目标对象, 可以返回目标对象本身也可以返回一个它的代理, 这将决定是否会进行拦截 */ @Override public Object plugin(Object target) { if (target instanceof Executor) { // 仅当对象为Executor时, 才使用本插件 return Plugin.wrap(target, this); } else { return target; } } /** * 用于配置本插件的相关属性 */ @Override public void setProperties(Properties properties) { // Do nothing } @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); Object target = invocation.getTarget(); //被代理对象 if (target instanceof Executor) { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = invocation.getArgs()[1]; BoundSql boundSql = mappedStatement.getBoundSql(parameter); String sql = boundSql.getSql(); logger.info("SQL: {}", sql); } Object result = invocation.proceed(); long duration = System.currentTimeMillis() - start; logger.info("Time elapsed: {}ms", duration); return result; } }
代码说明
@Intercepts 注解: 这个地方配置需要拦截的对象方法, 每个方法对应一个Signature, 这个Signature由对象类型, 方法名和参数组成, 方法名和参数可以直接参考这个对象的接口.
Object plugin(Object target)接口: 用于封装目标对象, 可以返回目标对象本身也可以返回一个它的代理, 这将决定是否会进行拦截
void setProperties(Properties properties)接口: 用于配置本插件的相关属性, 值可以通过Mybatis配置文件传入
Object intercept(Invocation invocation) throws Throwable接口: 执行拦截的方法, 其中 invocation.getTarget() 可以看到实际被代理的对象, 根据对象类型不同, 可以读取这个对象方法的参数并进行需要的操作.