点击蓝色“程序猿DD”关注我哟

加个“星标”,不忘签到哦

转载自公众号:日拱一兵


关注我,回复口令获取可获取独家整理的学习资料:

001 :领取《Spring Boot基础教程》

002 :领取《Spring Cloud基础教程》

>>当当大促,160买400的书,点击进入<<

拦截器介绍

Mybatis Interceptor 在 Mybatis 中被当作 Plugin(插件),不知道为什么,但确实是在 org.apache.ibatis.plugin 包下面。

既然是拦截器,可以拦截哪些内容呢?试想一下...... 当程序写到持久层时,Mybatis 会 执行 指定 SQL 语句,并处理 请求参数 和 返回值。没错,Mybatis 拦截器可以帮助我们处理上述内容,请看官网的 Plugins 的片段, 内容不多

// 执行
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
// 请求参数处理
ParameterHandler (getParameterObject, setParameters)
// 返回结果集处理
ResultSetHandler (handleResultSets, handleOutputParameters)
// SQL语句构建
StatementHandler (prepare, parameterize, batch, update, query)

拦截器的使用

如果需要实现自定义的拦截器,只需要实现 org.apache.ibatis.plugin.Interceptor 接口,该接口有三个方法:

Object intercept(Invocation invocation) throws Throwable;    Object plugin(Object target);   void setProperties(Properties properties);

我们要实现数据加密,进入数据库的字段不能是真实的数据,但是返回来的数据要真实可用,所以我们需要针对 Parameter 和 ResultSet 两种类型处理,同时为了更灵活的使用,我们需要自定义注解

自定义注解

类注解,将注解放在实体类上

/**    * 需要加解密的类注解 */
@Documented
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptClass {
}

字段注解,将注解放在实体字段上

/**  * 加密字段注解    */
@Documented
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptField {    }

有了这两个注解,我们可以在我们可以标记我们要处理的实体和实体中的字段

自定义参数处理拦截器

参考官网,通过 @Intercepts 和 @Signature 的联合使用,指定 ParameterHandler.class 类型,同时通过 @Component注解注入到容器中,即可在设置参数的时候进行拦截,通过自定义接口 IEncryptDecrypt, 根据 Field 的各种类型自定义加密解密算法

@Intercepts({    @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@ConditionalOnProperty(value = "domain.encrypt", havingValue = "true")
@Component
@Slf4j
public class ParammeterInterceptor  implements Interceptor {    @Autowired private IEncryptDecrypt encryptDecrypt; @Override  public Object intercept(Invocation invocation) throws Throwable {   log.info("拦截器ParamInterceptor");  //拦截 ParameterHandler 的 setParameters 方法 动态设置参数 if (invocation.getTarget() instanceof ParameterHandler) {   ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];    // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射  /*Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");    boundSqlField.setAccessible(true);  BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);*/  // 反射获取 参数对像    Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");  parameterField.setAccessible(true); Object parameterObject = parameterField.get(parameterHandler); if (Objects.nonNull(parameterObject)){  Class<?> parameterObjectClass = parameterObject.getClass();  EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class); if (Objects.nonNull(encryptDecryptClass)){  Field[] declaredFields = parameterObjectClass.getDeclaredFields(); final Object encrypt = encryptDecrypt.encrypt(declaredFields, parameterObject);    }   }   }   return invocation.proceed();    }   @Override  public Object plugin(Object o) {    return Plugin.wrap(o, this);    }   @Override  public void setProperties(Properties properties) {  }
}

同样新建结果集拦截器

结果集拦截器

与参数拦截器基本一样, 只不过类型指定为 ResultSetHandler.class

@Intercepts({  @Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})
})
@ConditionalOnProperty(value = "domain.decrypt", havingValue = "true")
@Component
@Slf4j
public class ResultInterceptor implements Interceptor { @Autowired private IEncryptDecrypt encryptDecrypt; @Override  public Object intercept(Invocation invocation) throws Throwable {   Object result = invocation.proceed();  if (Objects.isNull(result)){    return null;    }   if (result instanceof ArrayList) {  ArrayList resultList = (ArrayList) result; if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))){    for (int i = 0; i < resultList.size(); i++) { encryptDecrypt.decrypt(resultList.get(i));  }   }   }else { if (needToDecrypt(result)){ encryptDecrypt.decrypt(result); }   }   return result;  }   public boolean needToDecrypt(Object object){    Class<?> objectClass = object.getClass();    EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);  if (Objects.nonNull(encryptDecryptClass)){  return true;    }   return false;   }   @Override  public Object plugin(Object target) {   return Plugin.wrap(target, this);   }   @Override  public void setProperties(Properties properties) {  }
}


加密解密接口

IEncryptDecrypt 接口定义了 加密和解密两个方法:

public interface IEncryptDecrypt {  /** * 加密方法  * @param declaredFields 反射bean成员变量 * @param parameterObject Mybatis入参 * @param <T> * @return  */  public <T> T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException;    /** * 解密方法  * @param result Mybatis 返回值,需要判断是否是ArrayList类型  * @param <T> * @return  */  public <T> T decrypt(T result) throws IllegalAccessException;
}

两个拦截器通过在 YAML 中配置属性,按条件注入,外加自定义加密解密算法,完成全局灵活的配置。

核心代码已上传至 Github Demo

问题彩蛋

也许应对当前的业务,看了该文章满足了当下需求,我们目前只看到了什么是 Mybatis 拦截器,怎样简单使用,拦截器的其他用法以及其他很多为什么都没有解决,关注公众号,回复“人迹罕至” 读完文章 「程序猿为什么要看源码」后 ,我不会满足眼前的这些基本应用,我会有诸多疑问,

  1. 我们日常写 CRUD 的业务,为什么 Executor 中只有 R(query) 和 U(update), 那么C(insert) 和 D(delete) 怎样处理的?

  2. 自定义拦截器是以什么方式被执行的,执行顺序是什么?

  3. 分页也是 Mybatis 拦截器的一种,带有分页的框架是怎样使用拦截器的呢?如 Mybatis PlusPageHelper

  4. 虽然重写了 Inteceptor 接口的 public void setProperties(Properties properties) 方法,但是并没有写什么业务逻辑,这个方法能怎样使用?

  5. ......

后续文章也会通过读源码的方式逐步解析这些问题,当然你有相关问题也可以留言交流讨论

提高效率工具

依旧推荐在写文章时用到的高效工具,后续相关工具也会在文章中陆续更新,请持续关注

MyBatis Log Plugin

MyBatis Log Plugin 是 Intelligj IDEA 的一个插件,用来从 Mybatis 输出的 log 中提取出当前调用的 SQL 语句,并将参数封装在 SQL 语句中组成完整的 SQL,这样,当我们调试的时候更加清晰方便,可以轻松定位是否 SQL 又问题


推荐阅读

  • Maven 虐我千百遍,我待 Maven 如初恋

  • 初探性能优化:2个月到4小时的性能提升

  • MySQL跑在CentOS 6 和 7上的性能比较

  • Spring Boot 配置文件中的花样,看这一篇足矣!

  • Mybatis的工作原理,你了解过吗?


活动介绍:自律到极致-人生才精致:第8期

活动奖励:《小灰的算法之旅》* 10

扫描下面二维码签到参与

关注我,加个星标,不忘签到哦~

2019

与大家聊聊技术人的斜杠生活

⬇️⬇️点一点小惊喜在等你

如何使用Mybatis的拦截器实现数据加密与解密相关推荐

  1. Mybatis Interceptor 拦截器原理 源码分析

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最 ...

  2. mybatis使用拦截器显示sql,使用druid配置连接信息

    mybatis使用拦截器显示sql,使用druid配置连接信息 mybatis sql Druid 1.显示出sql内容: 新建2个类: MybatisInterceptor :拦截sql,并获得输出 ...

  3. Mybatis 通过拦截器动态修改SQL

    01 使用场景 当我们在多租户的项目中,编写SQL语句都要带上tenant字段,用于区分不同的租户只能操作自己的数据. 比如,像下面的SQL select * from member where id ...

  4. mybatis 自定义拦截器

    拦截器注解 mybatis自定义拦截器实现步骤: 实现org.apache.ibatis.plugin.Interceptor接口. 添加拦截器注解org.apache.ibatis.plugin.I ...

  5. 一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页

              mybatis 分页详解.mybatis分页查询,mybatis分页拦截器使用.struts2下mybatis分页 mybatis默认是支持分页的,内部通过创建可滚动的Result ...

  6. 使用拦截器进行数据加解密

    文章目录 使用拦截器进行数据加解密 1. 加解密工具 3. 加解密字段注解 3.1 加密注解 3.2 解密注解 4. 封装加解密工具 5. 拦截器 6. 不同框架配置说明 6.1 springboot ...

  7. Mybatis SQL拦截器实现

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文原文阅读:https:/ ...

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

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

  9. Mybatis Interceptor 拦截器

    拦截器(Interceptor)在 Mybatis 中被当做插件(plugin)对待,官方文档提供了 Executor(拦截执行器的方法),ParameterHandler(拦截参数的处理),Resu ...

最新文章

  1. zookeeper集群启动报错:Cannot open channel to * at election address /ip:3888
  2. 【Netty】从 BIO、NIO 聊到 Netty
  3. Shell 与Python的交互
  4. 数据中心IT机房末端气流组织管理
  5. hdu5387(模拟)
  6. pc控制iphone的软件_如何通过“共享文件夹”实现iPhone与PC间文件快速传输
  7. hadoop学习3 查找块的位置
  8. 【基础】嵌入式浏览器移植基本要素
  9. java里pom.xml是啥意思_pom.xml详解
  10. rhel 6.4 增加光盘为yum repo
  11. [C++]实现10以内整数的简单科学计算器
  12. 人工智能教学解决方案
  13. 科学的软件测试培训时间是多久?
  14. 运放实现方波三角波发生器-总结报告
  15. 数据防泄密·工控安全保障方案
  16. 数字变成大写的类,把人民币转化为大写汉字
  17. 最新 android系统 设备 分布情况,CNCERT 2018年第一季度国内操作系统及浏览器占比情况分析...
  18. Java基础知识入门级!
  19. 2021校园PHP表白墙程序源码
  20. 投影串口测试程序_关于串口控制投影机的操作方法的几个步骤

热门文章

  1. linux shell 小数计算
  2. linux flatpak 简介 同一个应用在不同linux发行版运行
  3. /lib64/libc.so.6: version `GLIBC_2.14' not found问题
  4. Cannot send session cache limiter - headers already sent错误解决方法
  5. 在Windows 8下成功安装.Net3.5的方法
  6. ubuntu c/c++ IDE编程环境
  7. Design Pattern - Observer(C#)
  8. mysql查询时间between and_Mysql中用between...and...查询日期时注意事项
  9. 删除svn同步文件目录
  10. 专题 11 IPC之管道