当前位置: 首页 > news >正文

网站栏目合理性做核酸检测收费标准

网站栏目合理性,做核酸检测收费标准,网页设计公司经营范围,访问网站慢SpringBoot集成MyBatis的SQL拦截器实战一、为什么需要SQL拦截器?二、MyBatis拦截器基础2.1 核心接口:Interceptor2.2 拦截目标与签名配置三、实战一:慢查询监控拦截器3.1 需求说明3.2 完整实现代码(1)拦截器类&#xf…

SpringBoot集成MyBatis的SQL拦截器实战

  • 一、为什么需要SQL拦截器?
  • 二、MyBatis拦截器基础
  • 2.1 核心接口:Interceptor
  • 2.2 拦截目标与签名配置
  • 三、实战一:慢查询监控拦截器
  • 3.1 需求说明
  • 3.2 完整实现代码
  • (1)拦截器类
  • (2)SpringBoot注册拦截器
  • (3)测试效果
  • 四、实战二:数据脱敏拦截器(敏感信息保护)
  • 4.1 需求说明
  • 4.2 完整实现代码
  • (1)自定义脱敏注解
  • (2)实体类添加注解
  • (3)脱敏工具类
  • (4)结果集拦截器
  • (5)注册多个拦截器
  • (6)测试效果
  • 五、实战踩坑指南
  • 5.1 拦截器顺序问题
  • 5.2 拦截器签名配置错误
  • 5.3 性能问题
  • 六、总结与扩展

一、为什么需要SQL拦截器?

先看几个真实场景:

慢查询监控:生产环境突然出现接口超时,需要快速定位执行时间过长的SQL 数据脱敏:用户表查询结果中的手机号、身份证号需要自动替换为**** 权限控制:多租户系统中,自动给SQL添加tenant_id = ?条件,防止数据越权访问 SQL审计:记录所有执行的SQL语句、执行人、执行时间,满足合规要求

如果没有拦截器,这些需求可能需要修改每一个Mapper接口或Service方法,工作量巨大。

而MyBatis的SQL拦截器能在SQL执行的各个阶段进行拦截处理,实现"无侵入式"增强。

二、MyBatis拦截器基础

2.1 核心接口:Interceptor

MyBatis的拦截器机制基于JDK动态代理,所有自定义拦截器都要实现Interceptor接口:

public interface Interceptor {// 拦截逻辑的核心方法Object intercept(Invocation invocation) throws Throwable;// 生成代理对象(通常直接用Plugin.wrap())Object plugin(Object target);// 读取配置参数(如从mybatis-config.xml中获取)void setProperties(Properties properties);
}

2.2 拦截目标与签名配置

MyBatis允许拦截4个核心组件的方法,通过@Intercepts和@Signature注解指定拦截目标:

||||

|---|---|---| |拦截类型|作用|常用拦截方法| |Executor|SQL执行器(最常用)|update、query、commit、rollback| |StatementHandler|SQL语句处理器(控制SQL生成)|prepare、parameterize| |ParameterHandler|参数处理器(处理SQL参数)|setParameters| |ResultSetHandler|结果集处理器(处理查询结果)|handleResultSets|

举个栗子:拦截StatementHandler的prepare方法(SQL预编译阶段):

@Intercepts({@Signature(type = StatementHandler.class,  // 拦截哪个接口method = "prepare",             // 拦截接口的哪个方法args = {Connection.class, Integer.class}  // 方法参数类型(用于确定重载方法))
})
public class MySqlInterceptor implements Interceptor {// 实现接口方法...
}

注意:args参数必须严格匹配方法的参数类型,否则拦截不到!比如prepare方法有两个重载,这里指定(Connection, Integer)类型的参数。

三、实战一:慢查询监控拦截器

3.1 需求说明

监控所有SQL执行时间,超过阈值(如500ms)则打印警告日志,包含:

  • SQL执行时间
  • 完整SQL语句(带参数占位符)
  • 参数值(防止SQL注入排查)

3.2 完整实现代码

(1)拦截器类

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;@Slf4j
@Intercepts({// 拦截查询方法@Signature(type = StatementHandler.class,method = "query",args = {Statement.class, ResultHandler.class}),// 拦截更新方法(insert/update/delete)@Signature(type = StatementHandler.class,method = "update",args = {Statement.class})
})
public class SlowSqlInterceptor implements Interceptor {// 慢查询阈值(毫秒),可通过配置文件注入private long slowThreshold = 500;@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 1. 记录开始时间long startTime = System.currentTimeMillis();try {// 2. 执行原方法(继续SQL执行流程)return invocation.proceed();} finally {// 3. 计算执行耗时(无论成功失败都记录)long costTime = System.currentTimeMillis() - startTime;// 4. 获取SQL语句和参数StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String sql = statementHandler.getBoundSql().getSql();  // 获取SQL语句(带?占位符)Object parameterObject = statementHandler.getBoundSql().getParameterObject();  // 获取参数// 5. 判断是否慢查询if (costTime > slowThreshold) {log.warn("[慢查询警告] 执行时间: {}ms, SQL: {}, 参数: {}", costTime, sql, parameterObject);} else {log.info("[SQL监控] 执行时间: {}ms, SQL: {}", costTime, sql);}}}@Overridepublic Object plugin(Object target) {// 生成代理对象(MyBatis提供的工具方法,避免自己写代理逻辑)return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 从配置文件读取阈值(如application.yml中配置)String threshold = properties.getProperty("slowThreshold");if (threshold != null) {slowThreshold = Long.parseLong(threshold);}}
}

(2)SpringBoot注册拦截器

package com.example.config;import com.example.interceptor.SensitiveInterceptor;
import com.example.interceptor.SlowSqlInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.Properties;@Configuration
@MapperScan("com.example.mapper")  // Mapper接口所在包
public class MyBatisConfig {// 注册慢查询拦截器@Beanpublic SlowSqlInterceptor slowSqlInterceptor() {SlowSqlInterceptor interceptor = new SlowSqlInterceptor();// 设置属性(也可通过application.yml配置)Properties properties = new Properties();properties.setProperty("slowThreshold", "500");  // 慢查询阈值500msinterceptor.setProperties(properties);return interceptor;}@Beanpublic SensitiveInterceptor sensitiveInterceptor() {return new SensitiveInterceptor();}// 将拦截器添加到SqlSessionFactory@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource, SlowSqlInterceptor slowSqlInterceptor) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);// 设置Mapper.xml路径(如果需要)/*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));*/// 添加拦截器sessionFactory.setPlugins(slowSqlInterceptor);return sessionFactory.getObject();}
}

(3)测试效果

写个简单的查询接口:

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public User getUserById(Long id) {return userMapper.selectById(id);}
}

执行后控制台输出:

[SQL监控] 执行时间: 30ms, SQL: SELECT id,username,phone FROM user WHERE id = ?

如果SQL执行时间超过500ms(比如查询大数据量表):

[慢查询警告] 执行时间: 1430ms, SQL: SELECT * FROM user WHERE id = ?, 参数: {id=1, param1=1}

踩坑提示:如果拦截不到SQL,检查@Signature注解的args参数是否与方法参数类型完全匹配!

四、实战二:数据脱敏拦截器(敏感信息保护)

4.1 需求说明

查询用户信息时,自动将敏感字段脱敏:

  • 手机号:13812345678 → 138****5678
  • 身份证号:110101199001011234 → ****************34

4.2 完整实现代码

(1)自定义脱敏注解

import java.lang.annotation.*;// 作用在字段上
@Target(ElementType.FIELD)
// 运行时生效
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {// 脱敏类型(手机号、身份证号等)SensitiveType type();
}// 脱敏类型枚举
public enum SensitiveType {PHONE,    // 手机号ID_CARD   // 身份证号
}

(2)实体类添加注解

import lombok.Data;@Data
public class User {private Long id;private String username;@Sensitive(type = SensitiveType.PHONE)  // 手机号脱敏private String phone;@Sensitive(type = SensitiveType.ID_CARD)  // 身份证号脱敏private String idCard;
}

(3)脱敏工具类

public class SensitiveUtils {// 手机号脱敏:保留前3位和后4位public static String maskPhone(String phone) {if (phone == null || phone.length() != 11) {return phone;  // 非手机号格式不处理}return phone.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2");}// 身份证号脱敏:保留最后2位public static String maskIdCard(String idCard) {if (idCard == null || idCard.length() < 18) {return idCard;  // 非身份证格式不处理}return idCard.replaceAll("\d{16}(\d{2})", "****************$1");}
}

(4)结果集拦截器

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;@Slf4j
@Intercepts({@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})
})
public class SensitiveInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 1. 执行原方法,获取查询结果Object result = invocation.proceed();// 2. 如果结果是List,遍历处理每个元素if (result instanceof List<?>) {List<?> resultList = (List<?>) result;for (Object obj : resultList) {// 3. 对有@Sensitive注解的字段进行脱敏desensitize(obj);}}return result;}// 反射处理对象中的敏感字段private void desensitize(Object obj) throws IllegalAccessException {if (obj == null) {return;}Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();  // 获取所有字段(包括私有)for (Field field : fields) {// 4. 检查字段是否有@Sensitive注解if (field.isAnnotationPresent(Sensitive.class)) {Sensitive annotation = field.getAnnotation(Sensitive.class);field.setAccessible(true);  // 开启私有字段访问权限Object value = field.get(obj);  // 获取字段值if (value instanceof String) {String strValue = (String) value;// 5. 根据脱敏类型处理switch (annotation.type()) {case PHONE:field.set(obj, SensitiveUtils.maskPhone(strValue));break;case ID_CARD:field.set(obj, SensitiveUtils.maskIdCard(strValue));break;default:break;}}}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可配置更多脱敏规则,此处省略}
}

(5)注册多个拦截器

修改MyBatisConfig,添加脱敏拦截器:

@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {// ... 慢查询拦截器配置 ...@Beanpublic SensitiveInterceptor sensitiveInterceptor() {return new SensitiveInterceptor();}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource, SlowSqlInterceptor slowSqlInterceptor,SensitiveInterceptor sensitiveInterceptor) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));// 注册多个拦截器(注意顺序!先执行的拦截器先注册)sessionFactory.setPlugins(slowSqlInterceptor, sensitiveInterceptor);return sessionFactory.getObject();}
}

(6)测试效果

查询用户信息:

User user = userService.getUserById(1L);
System.out.println(user); 
// 输出:User(id=1, username=张三, phone=138****5678, idCard=****************34)

五、实战踩坑指南

5.1 拦截器顺序问题

:多个拦截器时,注册顺序就是执行顺序。比如先注册慢查询拦截器,再注册脱敏拦截器:

SQL执行 → 慢查询拦截器(记录时间) → 脱敏拦截器(处理结果)

如果顺序反了,脱敏拦截器会先处理结果,慢查询拦截器记录的SQL就看不到原始参数了。

解决:按"执行SQL前→执行SQL后→处理结果"的顺序注册。

5.2 拦截器签名配置错误

:@Signature的args参数类型写错,导致拦截不到方法。比如StatementHandler.prepare方法有两个重载:

// 正确的参数类型
prepare(Connection connection, Integer transactionTimeout)
// 错误示例:写成了(int)
@Signature(args = {Connection.class, int.class})  // 出现下面的异常!java.lang.NoSuchMethodException: org.apache.ibatis.executor.statement.StatementHandler.prepare(java.sql.Connection,int)

解决:通过IDE查看方法参数类型,确保完全一致。

5.3 性能问题

:在拦截器中做复杂操作(如反射遍历所有字段)会影响性能。

解决

  • 反射操作缓存Class信息
  • 非必要不拦截(如只拦截查询方法)
  • 敏感字段脱敏可考虑在DTO层处理

六、总结与扩展

通过SQL拦截器,我们用极少的代码实现了SQL监控和数据脱敏,避免了修改大量业务代码。

http://www.dtcms.com/a/400325.html

相关文章:

  • Linux 性能瓶颈排查思路
  • MySQL深分页优化及试验
  • 外贸网站案例柳州建设网站
  • 深圳个人网站制作微网站建设价格对比
  • 高校教资--高等教育学
  • 源码建站和模板建站区别seo技巧是什么意思
  • 郑州网站公司哪家好重庆网站建设 熊掌号
  • 室温太赫兹设备打开了6G网络的大门
  • 【源码解析】spring-ai-alibaba-jmanus的ModelDataInitialization
  • 广联达 CONCETTO 产品与分析
  • 比较版本号-双指针比较
  • 网站推广seo代理长春广告设计公司
  • wordpress建站后怎样发布手绘动画制作软件
  • ppt模板做的好的网站有哪些品牌建设ppt
  • 行业电子网站建设长葛做网站
  • 2025版基于springboot的家政服务预约系统
  • agent概述
  • 万网虚拟主机上传网站php网站怎么做集群
  • 深圳网站优化包年wordpress footer.php
  • 网站备案安全承诺书公司名称大全及最新
  • 厦门网站建设方案咨询大连企业网站建设定制
  • Android Studio 详细安装与配置指南
  • 陶瓷类网站建设个人网站设计论文一万字
  • 带数据库的网站模板下载做网站网页的成本
  • 免费网站排名优化在线域名设计与分析
  • 漂亮公司网站源码打包下载大型户外广告设计公司
  • 网站建设 分类wordpress默认index
  • 免费建微网站平台网站换程序 搜索引擎
  • 门户网站建设滞后怎样做网站内链
  • 数字化转型:概念性名词浅谈(第五十讲)