MyBatis拦截器可以用来拦截参数、拦截Sql、拦截结果集等方式。
可以用来处理多租户、参数编码解码、参数过滤、sql监控等业务。
主要实现方式是由Mybatis提供的注解,并将类配置活注入到springBean中去
参考文章:MyBatis拦截器原理
业务需求:sql中的Like如果传参为%则会造成sql查询失效,传入的是%但是还是可以查到所有数据,不符合业务场景
- 创建查询参数拦截器Interceptor
package com.spm.common.security.interceptor;
import com.spm.common.core.utils.StringUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
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.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.sql.PreparedStatement;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* @description:
* @author: ch
* @time: 2022/7/1 16:07
*/
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)
})
//@Intercepts标明这是一个Mybatis拦截器类
//@Signature为具体拦截的位置 ParameterHandler为拦截参数
public class QueryExecutorInterceptor implements Interceptor {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取拦截器拦截的设置参数对象DefaultParameterHandler
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
// 通过mybatis的反射来获取对应的值
MetaObject metaResultSetHandler = MetaObject.forObject(parameterHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
Object parameterObject = metaResultSetHandler.getValue("parameterObject");
if (null != parameterObject) {
//通过java反射获取到对象的读写方法
List<PropertyDescriptor> propertiesList = getPropertiesList(parameterObject.getClass());
Method readMethod;
Method writeMethod;
for (PropertyDescriptor property : propertiesList) {
readMethod = property.getReadMethod();
writeMethod = property.getWriteMethod();
Object invoke = readMethod.invoke(parameterObject);
if (null != invoke) {
String data = invoke.toString();
if (StringUtils.contains(data, "%")) {
System.out.println("拦截到参数为%号数据,已修改!");
writeMethod.invoke(parameterObject, StringUtils.escapeField(data));
}
}
}
// 回写parameterObject对象
metaResultSetHandler.setValue("parameterObject", parameterObject);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 获取对象字段集合
*
* @param clazz 需要反射的对象类型
* @return
* @throws IntrospectionException
*/
public static List<PropertyDescriptor> getPropertiesList(Class<?> clazz) throws IntrospectionException {
List<PropertyDescriptor> descriptors = Arrays.stream(Introspector.getBeanInfo(clazz)
.getPropertyDescriptors()).filter(p -> {
String name = p.getName();
//过滤掉不需要修改的属性
return !"class".equals(name) && !"id".equals(name);
}).collect(Collectors.toList());
return descriptors;
}
}
2.将拦截器注入到SpringBean工厂(我这块是用的spring.factories的注入方式,@Component应该也可以,只要将配置类注入就行)
package com.spm.common.security.config;
import com.spm.common.security.interceptor.QueryExecutorInterceptor;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description:
* @author: ch
* @time: 2022/7/1 17:22
*/
public class MybatisConfig {
@Bean
ConfigurationCustomizer mybatisConfigurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.addInterceptor(new QueryExecutorInterceptor());
}
};
}
}
给百分号前边添加”\“转义符
通过拦截器解决要比手动在每个Controller毕竟更加简介明了,但是拦截和反射会消耗程序性能!