作者:溪~源
blog.csdn.net/Xuan _ Lu/article/details/109253213
首先熟悉Mybatis的运行过程,如下图所示。
首先,说明Mybatis可以监听的类型具体有以下4种。
执行程序:如何阻止执行机构。
参数处理程序:阻止参数的过程。
结果处理程序:阻止处理结果集。
语句处理程序:阻止处理SQL语法生成。
规则Intercepts注释需要Signature截取点参数数组。 在Signature中,指定要阻止哪个对象中的哪个方法。 @Intercepts注释定义如下:
@ documented @ retention (retention policy.runtime ) target ) elementtype.type ) public @ interface intercepts {/* * } 定义如下。
@ documented @ retention (retention policy.runtime ) target({} ) public@interfacesignature(/***拦截的类执行程序,PPP /**在定义拦截类的基础上,拦截的方法*/Stringmethod (); /**在定义拦截方法的基础上,定义拦截方法对应的参数。 *注意参数类型和顺序*/Class,因为*JAVA可能会重载方法? []args (; }标识拦截注释@Intercepts规则的使用。 以下是一个简单的例子。
@intercepts(//请注意这个大括号。 也就是说,您可以在此定义多个@Signature并监听到多个位置。 所有的都使用这个拦截器@ signature (type=resultset handler.class,method='Handdler ) args={Statement.class},@ signgind
@Signature :显示自定义拦截器需要拦截哪些类型、哪些方法;
类型:上述四种类型中的一种;
method (因为可能存在对应于接口的什么类型的方法()重载方法);
args :应对哪些方法的参与;
method如何支持四种类型:
介绍拦截器定制的实践部分,主要遵循以下三个步骤。
1 .实现org.Apache.ibatis.plugin.interceptor接口,然后重写以下方法:
publicinterfaceinterceptor { object intercept (invocation var1) throwsThrowable; 对象插件(objectvar 1; 语音属性(属性var 1; } 2.添加拦截器注释@Intercepts{.}。 具体值按照上述规则设定。
3 .向配置文件中添加阻止程序。
Invocation invocation (Intercept )或更高版本表明,有四种类型的对象可供interceptor拦截。 其中,加入invocation是指监听到的对象。
示例:如果阻止StatementHandler#query(Statement st,ResultHandler rh)方法,则Invocation适用于该方法。
plugin (对象目标)的方法的作用是使mybatis判断是否监听,并决定是否生成代理。
@ overridepublicobjectplugin ((object target )//确定是否侦听这种类型的对象) @由@Intercepts注释确定),然后返回代理对象或原始对象//,所以我们在实现plugin方法时,可以确定目标类型,如果是插件
拦截的对象时才执行Plugin.wrap方法,否则的话,直接返回目标本身。 if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target;}注意:每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。
setProperties(Properties properties)拦截器需要一些变量对象,而且这个对象是支持可配置的。(搜索公众号Java知音,回复“2021”,送你一份Java面试题宝典)
实战自定义拦截器
@Intercepts(value = {@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); Object obj = boundSql.getParameterObject(); String sql = boundSql.getSql(); if (sql.trim().toUpperCase().startsWith("INSERT")) { ReflectUtil.setFieldValue(obj, "rev", 0); ReflectUtil.setFieldValue(obj, "createTime", new Date()); ReflectUtil.setFieldValue(obj, "operateTime", new Date()); ReflectUtil.setFieldValue(boundSql,"parameterObject", obj); } else if (sql.trim().toUpperCase().startsWith("UPDATE")) { sql = sql.replaceAll(" set ", " SET ") .replaceAll(" Set ", " SET ") .replaceAll(" SET ", " SET rev = rev+1, operate_time = NOW(), "); ReflectUtil.setFieldValue(boundSql,"sql", sql); } return invocation.proceed(); } @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } @Override public void setProperties(Properties properties) { }}主要看下核心代码方法intercept():
这段代码主要目的:拦截insert和update语句,利用反射机制,设置insert语句的参数rev(版本号,利用乐观锁),第一次查询,故创建时间和操作时间相同;update是将版本号+1,统一修改其操作时间。
mybatis-config
<?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> <plugins> <plugin interceptor="com.qxy.mybatis.interceptor.MyInterceptor"/> </plugins></configuration>application.yml
特别重要的一点,一定将mybatis-config中的对象注入到Sprint容器中,否则不会生效。
...//省略其他配置mybatis: config-location: classpath:/mybatis-config.xmlReflectUtil
public class ReflectUtil { private ReflectUtil() {} /** * 利用反射获取指定对象的指定属性 * @param obj 目标对象 * @param fieldName 目标属性 * @return 目标字段 */ private static Field getField(Object obj, String fieldName) { Field field = null; for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { try { field = clazz.getDeclaredField(fieldName); break; } catch (NoSuchFieldException e) { //这里不用做处理,子类没有该字段,可能父类有,都没有就返回null } } return field; } /** * 利用反射设置指定对象的指定属性为指定的值 * @param obj 目标对象 * @param fieldName 目标属性 * @param fieldValue 目标值 */ public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws IllegalAccessException { Field field = getField(obj, fieldName); if (field != null) { field.setAccessible(true); field.set(obj, fieldValue); } }}debug
上图中能够看到BoundSql对象中主要存储的属性值,所以我们自定义拦截器时,主要针对BoundSql的属性值进行修改。
程序代码没有走到我们反射机制设置值的位置,测试createTime=null;
返回之前,看下BoundSql对象的值,创建时间已被赋值。
END 推荐好文 >>【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能>>分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!>>能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!