文章目录
介绍
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。比如执行前、执行后或者对SQL结果集处理、sql入参处理等,这样就可以在不修改mybatis源码的情况下对sql执行的过程或结果进行修改,实现了解耦。mybatis 是在动态代理的基础上实现的。对动态代理感兴趣的可以看下JAVA 代理模式不了解一下吗?
使用场景
如果业务中需要设置一些通用数据库操作,比如创建时间、创建人等通用字段又或者是分页操作等,这类都可以使用插件开发方式,PageHelper就是基于Interceptor的一个mybatis插件。
Interceptor拦截器
public interface Interceptor { /** * 子类拦截器必须要实现的方法, * 在该方法对内自定义拦截逻辑 * @param invocation * @return * @throws Throwable */ Object intercept(Invocation invocation) throws Throwable; /** 生成目标类的代理对象 * 也可以根据需求不返回代理对象,这种情况下这个拦截器将不起作用 * 无特殊情况使用默认的即可 * @param target * @return */ default Object plugin(Object target) { return Plugin.wrap(target, this); } /** * 设置变量 * 在注册拦截器的时候设置变量,在这里可以获取到 * @param properties */ default void setProperties(Properties properties) { // NOP } }
InterceptorChain拦截器链
在org.apache.ibatis.plugin包下有个InterceptorChain类,该类有个interceptors属性,所有实现了Interceptor接口的拦截器都会被存储到interceptors中。
源码如下:
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); /** * 让目标类在所有的拦截器中生成代理对象,并返回代理对象 * @param target * @return */ public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } /** * 添加过滤器 * @param interceptor */ public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } }
拦截方法
默认情况下,MyBatis 允许使用插件来拦截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系统中有配置自定义插件,默认情况下,系统会把上面四个类的默认子类都作为目标类来让所有的拦截器进行拦截,
以保证所有的拦截器都能对Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子类进行拦截。
源码如下:
在org.apache.ibatis.session.Configuration类中
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 使用拦截器进行拦截 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); // 使用拦截器进行拦截 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 使用拦截器进行拦截 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } public Executor newExecutor(Transaction transaction) { return newExecutor(transaction, defaultExecutorType); } public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } // 使用拦截器进行拦截 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
注解
Intercepts
Intercepts的作用是拦截Signature注解数组中指定的类的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Intercepts { /** * Returns method signatures to intercept. * Signature注解列表 * @return method signatures */ Signature[] value(); }
Signature
Signature注解作用是拦截指定类的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Signature { /** * Returns the java type. * 要拦截的类 * @return the java type */ Class<?> type(); /** * Returns the method name. * 要拦截的类的方法 * @return the method name */ String method(); /** * Returns java types for method argument. * 要拦截的类的方法的参数列表 * @return java types for method argument */ Class<?>[] args(); }
示例
步骤
1、实现org.apache.ibatis.plugin.Interceptor接口
2、添加Intercepts和Signature注解
3、根据需求实现Interceptor方法逻辑
入门使用
这里会写两个使用示例,一个是动态给属性赋值,一个是打印SQL。
表结构:
CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `gender` varchar(20) DEFAULT NULL, `userName` text NOT NULL, `create_date` datetime DEFAULT NULL COMMENT '创建日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
实体类:
public class UserInfo { private Long id; private String gender; private String userName; private Date createDate; // 省略get、set方法 }