1. 拦截器注解

    1. mybatis自定义拦截器实现步骤:

      1. 实现org.apache.ibatis.plugin.Interceptor接口。
      2. 添加拦截器注解org.apache.ibatis.plugin.Intercepts。
      3. 配置文件中添加拦截器。
    2. 在mybatis中可被拦截的类型有四种(按照拦截顺序):

      1. Executor: 拦截执行器的方法。
      2. ParameterHandler: 拦截参数的处理。
      3. ResultHandler:拦截结果集的处理。
      4. StatementHandler: 拦截Sql语法构建的处理。
    3. 拦截器注解的作用:
      自定义拦截器必须使用mybatis提供的注解来声明我们要拦截的类型对象。Mybatis插件都要有Intercepts 注解来指定要拦截哪个对象哪个方法。我们知道,Plugin.wrap()方法会返回四大接口对象的代理对象,会拦截所有的方法。在代理对象执行对应方法的时候,会调用InvocationHandler处理器的invoke方法。

    4. 拦截器注解的规则:

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
 1. @Intercepts:标识该类是一个拦截器;@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;2.1 type:对应四种类型中的一种;2.2 method:对应接口中的哪类方法(因为可能存在重载方法);2.3 args:对应哪一个方法;
  1. 拦截器可拦截的方法:
    |拦截的类| 拦截的方法 |
    |Executor|update, query, flushStatements, commit, rollback,getTransaction, close, isClosed|
    |ParameterHandler | getParameterObject, setParameters |
    |StatementHandler|prepare, parameterize, batch, update, query|
    |ResultSetHandler|handleResultSets, handleOutputParameters|

  2. 拦截器方法
    6.1 官方插件开发方式

@Intercepts({@Signature(type = Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {public Object intercept(Invocation invocation) throws Throwable {Object target = invocation.getTarget(); //被代理对象Method method = invocation.getMethod(); //代理方法Object[] args = invocation.getArgs(); //方法参数// do something ...... 方法拦截前执行代码块Object result = invocation.proceed();// do something .......方法拦截后执行代码块return result;}public Object plugin(Object target) {return Plugin.wrap(target, this);}
}

拦截 Executor 示例:

@Intercepts(@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class}))
@Component
public class MyInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object[] args = invocation.getArgs();MappedStatement ms = (MappedStatement) args[0];SqlCommandType sqlCommandType = ms.getSqlCommandType();if (!SqlCommandType.INSERT.equals(sqlCommandType)&&!SqlCommandType.UPDATE.equals(sqlCommandType)){return invocation.proceed();}Object parameters = args[1];if (parameters instanceof Map){Map<String,Object> paramMap = (Map<String, Object>) parameters;if (ObjectUtils.isEmpty(paramMap.get("createTime"))){paramMap.put("createTime",new Date());}if (ObjectUtils.isEmpty(paramMap.get("updateTime"))){paramMap.put("updateTime",new Date());}}else{Class<?> aClass = parameters.getClass();Field createTimeField = aClass.getDeclaredField("createTime");createTimeField.setAccessible(true);Object createObj = createTimeField.get(parameters);if(ObjectUtils.isEmpty(createObj)){createTimeField.set(parameters,new Date());}Field updateTimeField = aClass.getDeclaredField("updateTime");updateTimeField.setAccessible(true);Object updateObj = updateTimeField.get(parameters);if(ObjectUtils.isEmpty(updateObj)){updateTimeField.set(parameters,new Date());}}return invocation.proceed();}@Overridepublic Object plugin(Object o) {return Plugin.wrap(o, this);}@Overridepublic void setProperties(Properties properties) {}
}

拦截StatementHandler 示例:

@Intercepts(@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class}))
@Slf4j
@Component
public class InterceptorConfig implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler)invocation.getTarget();MetaObject metaObject = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");BoundSql boundSql = statementHandler.getBoundSql();SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();log.info("方法执行前");if (SqlCommandType.SELECT.equals(sqlCommandType)){String sql = boundSql.getSql();log.info("sql before:"+ sql);sql = sql+" where is_delete = 0";log.info("sql after:"+ sql);Field sqlField = boundSql.getClass().getDeclaredField("sql");sqlField.setAccessible(true);sqlField.set(boundSql,sql);}return invocation.proceed();}@Overridepublic Object plugin(Object o) {return Plugin.wrap(o,this);}@Overridepublic void setProperties(Properties properties) {}
}

6.2 拦截器的方法

public interface Interceptor {   Object intercept(Invocation invocation) throws Throwable;       Object plugin(Object target);    void setProperties(Properties properties);
}

setProperties方法
如果我们的拦截器需要一些变量对象,而且这个对象是支持可配置的。
类似于Spring中的@Value("${}")从application.properties文件中获取。
使用方法:

<plugin interceptor="com.plugin.mybatis.MyInterceptor"><property name="username" value="xxx"/><property name="password" value="xxx"/>
</plugin>

方法中获取参数:properties.getProperty(“username”);

问题:但是为什么不直接使用@Value("${}") 获取变量?

解答:因为mybatis框架本身就是一个可以独立使用的框架,没有像Spring这种做了很多的依赖注入。

plugin方法
这个方法的作用是就是让mybatis判断,是否要进行拦截,然后做出决定是否生成一个代理。

    @Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}

需要注意的是:每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。

问题1:Plugin.wrap(target, this)方法的作用?

解答:判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。

故我们在实现plugin方法时,要判断一下目标类型,是本插件要拦截的对象时才执行Plugin.wrap方法,否则的话,直接返回目标本身。

intercept(Invocation invocation)方法
我们知道,mybatis只能拦截四种类型的对象。而intercept方法便是处理拦截到的对象。比如我们要拦截StatementHandler#query(Statement st,ResultHandler rh)方法,那么Invocation就是这个对象,Invocation中有三个参数。

target:StatementHandler;
method :query;
args[]:Statement st,ResultHandler rh
org.apache.ibatis.reflection.SystemMetaObject#forObject:方便的获取对象中的值。

案例:将参数拼接到sql语句。

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
@Slf4j
public class Aa implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Statement statement;//获取方法参数Object firstArg = invocation.getArgs()[0];if (Proxy.isProxyClass(firstArg.getClass())) {statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");} else {statement = (Statement) firstArg;}MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);//获取Statement对象(sql语法已经构建完毕)statement = (Statement) stmtMetaObj.getValue("stmt.statement");//获取sql语句String originalSql = statement.toString();//将原始sql中的空白字符(\s包括换行符,制表符,空格符)替换为" "originalSql = originalSql.replaceAll("[\\s]+", " ");//只获取sql的select/update/insert/delete开头的sqlint index = indexOfSqlStart(originalSql);if (index > 0) {originalSql = originalSql.substring(index);}// 计算执行 SQL 耗时long start = System.currentTimeMillis();Object result = invocation.proceed();long timing = System.currentTimeMillis()- start;//获取MapperStatement对象,获取到sql的详细信息Object realTarget = realTarget(invocation.getTarget());//获取metaObject对象MetaObject metaObject = SystemMetaObject.forObject(realTarget);//获取MappedStatement对象MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");StringBuilder formatSql = new StringBuilder().append(" Time:").append(timing)//获取Mapper信息和方法信息.append(" ms - ID:").append(ms.getId()).append("Execute SQL:").append(originalSql);//打印sql信息log.info(formatSql.toString());return result;}@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}@Overridepublic void setProperties(Properties prop) {}/*** 获取sql语句开头部分** @param sql* @return*/private int indexOfSqlStart(String sql) {String upperCaseSql = sql.toUpperCase();Set<Integer> set = new HashSet<>();set.add(upperCaseSql.indexOf("SELECT "));set.add(upperCaseSql.indexOf("UPDATE "));set.add(upperCaseSql.indexOf("INSERT "));set.add(upperCaseSql.indexOf("DELETE "));set.remove(-1);if (CollectionUtils.isEmpty(set)) {return -1;}List<Integer> list = new ArrayList<>(set);list.sort(Comparator.naturalOrder());return list.get(0);}/*** <p>* 获得真正的处理对象,可能多层代理.* </p>*/@SuppressWarnings("unchecked")public static <T> T realTarget(Object target) {if (Proxy.isProxyClass(target.getClass())) {MetaObject metaObject = SystemMetaObject.forObject(target);return realTarget(metaObject.getValue("h.target"));}return (T) target;}
}

mybatis 自定义拦截器相关推荐

  1. 使用mybatis plus自定义拦截器,实现数据权限

    需求 为了增强程序的安全性,需要在用户访问数据库的时候进行权限判断后选择性进行判断是否需要增强sql,来达到限制低级别权限用户访问数据的目的. 根据业务需要,这里将角色按照数据范围做权限限定.比如,角 ...

  2. 如何使用Mybatis的拦截器实现数据加密与解密

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 转载自公众号:日拱一兵 关注我,回复口令获取可获取独家整理的学习资料: - 001 :领取<Sp ...

  3. MyBatis框架 拦截器简单使用

    Interceptor 是MyBatis提供的一个插件(plugin扩展).代表拦截器,可以拦截代码中的数据库访问操作,即Statement操作 拦截后,可以去修改正在执行的SQL语句,可以额外访问数 ...

  4. Hadoop生态圈-Flume的组件之自定义拦截器(interceptor)

    Hadoop生态圈-Flume的组件之自定义拦截器(interceptor) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客只是举例了一个自定义拦截器的方法,测试字节传输速 ...

  5. web开发(二十一)之自定义拦截器的使用

    转自博客:http://blog.csdn.net/pkgk2013/article/details/51985817 拦截器的作用 拦截器,在AOP(Aspect-Oriented Programm ...

  6. SpringMVC——自定义拦截器、异常处理以及父子容器配置

    SpringMVC--自定义拦截器.异常处理以及父子容器配置 参考文章: (1)SpringMVC--自定义拦截器.异常处理以及父子容器配置 (2)https://www.cnblogs.com/so ...

  7. Struts2自定义类型转换器、自定义拦截器和用户输入数据的验证

    一.自定义类型转换器 1.编写一个类,继承com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter 2.覆盖掉其中的public Obj ...

  8. WebServices中使用cxf开发日志拦截器以及自定义拦截器

    首先下载一个cxf实例,里面包含cxf的jar包.我下的是apache-cxf-2.5.9 1.为什么要设置拦截器? 为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截 ...

  9. 自定义拦截器和提供的拦截器

    在访问struts2中某个action之后或者之前,会自动调用的类,就是struts2中的拦截器 一,怎样自定义拦截器 自定义一个拦截器需要三步: 1 ,自定义一个实现Interceptor接口(或者 ...

最新文章

  1. [转]ASP.NET 缓存(十六)--检索缓存项的值
  2. 命名空间不能直接包含_php命名空间
  3. WindowsPhone设置启动欢迎页面
  4. 基于纤程(Fiber)实现C++异步编程库(一):原理及示例
  5. WebLogic调用WebService提示Failed to localize、Failed to create WsdlDefinitionFeature
  6. mariadb不能导入与mysql可以,mysql/mariadb知识点总结(12):insert语句总结
  7. html登录之后注销,注销.html · NFUNM032/APP_CMS - Gitee.com
  8. 独立站引流真的那么难吗?
  9. Codeforces914D Bash and a Tough Math Puzzle
  10. javaSE_06Java中的数组(array)-思维导图
  11. 网络协议 3 - 从物理层到 MAC 层
  12. setInterval 和$interval的区别
  13. java io 创建临时文件,用Java创建一个指定的临时文件
  14. ThinkPHP截取部分文章文字、字符串
  15. 防火墙、防病毒网关、IDS以及该类安全产品开发
  16. 日常开支记账,自动统计用它就够了
  17. oracle视图在查询里,oracle视图
  18. laravel 验证手机号
  19. 深入浅出 - Android系统移植与平台开发(十)- Android编译系统与定制Android平台系统
  20. AlphaGo功成身退了,围棋还将继续

热门文章

  1. “高高兴兴上班,平平安安回家”
  2. 自定义UDF函数:将汉字转换成拼音
  3. 如何扩充C盘空间,不需要删除其余盘的任何东西。
  4. 新一代网络技术与课程建设师资培训感悟
  5. textarea内容换行,textarea中换行符br失效
  6. 【数据挖掘】关联规则的术语及定义
  7. 正则表达式语法及常用实例
  8. 1M带宽服务器能够承载多少人
  9. 物联网专业可以插本计算机技术吗,物联网工程专业专接本考什么
  10. jmeter压力测试报告