在 Java 的 Spring Boot + MyBatis 或 Spring MVC 项目中,可以通过 拦截器(Interceptor) 来拦截请求、修改 SQL 语句、记录日志等。拦截器的使用方式因不同的场景而有所不同。以下是几种常见的自定义拦截器实现方式。
1.MyBatis 拦截器(拦截 SQL 执行)
MyBatis 提供拦截器机制,可以拦截 SQL 执行前后,实现 SQL 解析、日志记录、权限控制、动态表名等功能。
1.1 创建mybatis拦截器
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
// @Intercepts 注解指定要拦截的方法
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyBatisSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取 StatementHandler
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// 获取 SQL
String sql = statementHandler.getBoundSql().getSql();
System.out.println("拦截的 SQL:" + sql);
// 这里可以修改 SQL,比如增加 WHERE 过滤条件
// 修改 SQL 逻辑(示例:追加一个 WHERE 条件)
// sql = sql + " AND user_id = 123";
return invocation.proceed(); // 继续执行 SQL
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this); // 代理
}
@Override
public void setProperties(Properties properties) {
// 这里可以读取配置
}
}
1.2 注册拦截器
在 Spring Boot + MyBatis 中,需要手动注册 MyBatis 插件
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Bean
public MyBatisSqlInterceptor sqlInterceptor() {
return new MyBatisSqlInterceptor();
}
@Bean
public SqlSessionFactory sqlSessionFactory(org.apache.ibatis.session.Configuration configuration) throws Exception {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory();
configuration.addInterceptor(sqlInterceptor()); // 添加拦截器
return sqlSessionFactory;
}
}
1.3 应用场景
sql日志:记录sql执行信息
权限控制:可以动态修改sql
自动分表:修改表名
数据脱敏:对查询的结果进行敏感信息处理
2. Spring MVC 拦截器(拦截 HTTP 请求)
如果需要 拦截 HTTP 请求(Controller 方法调用前后),可以使用 Spring MVC 拦截器。
2.1 创建spring MVC 拦截器
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyHttpInterceptor implements HandlerInterceptor {
// 在 Controller 方法调用前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截 HTTP 请求:" + request.getRequestURI());
// 进行权限验证
String token = request.getHeader("Authorization");
if (token == null || !token.equals("VALID_TOKEN")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return false; // 拦截请求,不继续执行
}
return true; // 继续执行
}
// 在 Controller 方法调用后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("Controller 处理完毕");
}
// 在视图渲染后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("HTTP 请求完成");
}
}
2.2 注册拦截器
在spring boot 配置类中注册拦截器
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyHttpInterceptor())
.addPathPatterns("/api/**") // 只拦截 /api/ 开头的路径
.excludePathPatterns("/api/login"); // 不拦截登录接口
}
}
应用场景:
权限控制:拦截未登录请求
日志记录:记录请求参数,响应时间
防止重复提交:拦截表单提交请求
3. Spring AOP 拦截器(拦截调用方法)
如若需要拦截所有Service/Contoller方法,可以使用Spring AOP
3.1 创建aop拦截器
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAopInterceptor {
@Around("execution(* com.example.service.*.*(..))") // 拦截 service 包下所有方法
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("方法执行前:" + joinPoint.getSignature().getName());
Object result = joinPoint.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("方法执行后:" + joinPoint.getSignature().getName() + ",耗时:" + (end - start) + "ms");
return result;
}
}
应用场景:
方法执行时间的统计
日志记录
参数校验