Java自定义拦截器

在 Java 的 Spring Boot + MyBatisSpring 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;
    }
}

应用场景:

方法执行时间的统计

日志记录

参数校验

拦截器类 需要两个配合使用这里只有一个 @Configuration public class ServletContextConfig extends WebMvcConfigurerAdapter { public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/"); super.addResourceHandlers(registry); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerMyInterceptorAdapter()).addPathPatterns("/**") .excludePathPatterns("/") // .excludePathPatterns("/expressions/getExpressionsList") .excludePathPatterns("/loginInfo/getCordByIsPhone") .excludePathPatterns("/loginInfo/login11") //token失效跳轉 .excludePathPatterns("/loginInfo/insertLoginInfo") //注册 .excludePathPatterns("/loginInfo/login") //登录 .excludePathPatterns("/upload") //上传文件 .excludePathPatterns("/uploadListen") //上传文件 .excludePathPatterns("/admin/user/goLogin") //后台跳转登录 .excludePathPatterns("/admin/user/login") //后台登录 .excludePathPatterns("/loginInfo/getLoginInfo") //忘记密码 .excludePathPatterns("/loginInfo/getCord") //短信验证码 .excludePathPatterns("/loginInfo/getIsLoginInfo") //判断验证码&&登录 .excludePathPatterns("/loginInfo/getIsLoginInfo1") //判断验证码 .excludePathPatterns("/loginInfo/setPassWord") //设置密码 ; } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } } @Component public class HandlerMyInterceptorAdapter implements HandlerInterceptor { @Autowired private HeartbeatServiceImpl heartbeatService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { String url = request.getRequestURI(); if (url.contains("admin")) { User user = (User) request.getSession().getAttribute("user"); try { if (user.equals(null)) { response.sendRedirect(serverConfig.SERVER + "admin/user/goLogin"); return false; } else { return true; } }catch (Exception e){ response.sendRedirect(serverConfig.SERVER + "admin/user/goLogin"); return false; } }else { String token = request.getHeader("token"); if (token != null) { Jedis jedis = new Jedis(com.sevenSteps.util.RedisConfig.HOST, RedisConfig.PORT); String s = jedis.get(token); if(token.equals(s)) { heartbeatService = SpringUtil.getBean(HeartbeatServiceImpl.class); return heartbeatService.setOutDate(token); }else { response.sendRedirect(serverConfig.SERVER + "loginInfo/login11"); return true; } }else { response.sendRedirect(serverConfig.SERVER + "loginInfo/login11"); return true; } } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
### Java 中实现自定义拦截器的方式 在 Java 开发中,可以通过多种框架和技术来实现自定义拦截器。以下是基于 MyBatis 和通用 Web 应用场景下的两种常见实现方式。 --- #### 一、MyBatis 自定义拦截器实现 MyBatis 提供了一种灵活的方式来创建自定义拦截器,主要依赖于 `Interceptor` 接口以及 `@Signature` 注解。下面是一个完整的实现示例: ##### 1. 定义拦截器类 ```java package com.example.interceptor; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import java.util.Properties; @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class CustomInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("Before execution of the intercepted method."); Object result = invocation.proceed(); System.out.println("After execution of the intercepted method."); return result; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 可以设置一些额外的属性到拦截器实例中 } } ``` 上述代码实现了对 `Executor.update(MappedStatement ms, Object parameter)` 方法的拦截[^1]。通过重写 `intercept()` 方法可以控制方法调用前后的逻辑处理。 ##### 2. 配置拦截器 为了使拦截器生效,需要将其注册到 MyBatis 的配置文件或程序中: ```xml <plugins> <plugin interceptor="com.example.interceptor.CustomInterceptor"/> </plugins> ``` 或者通过编程方式进行注册: ```java Configuration configuration = sqlSessionFactory.getConfiguration(); configuration.addInterceptor(new CustomInterceptor()); ``` --- #### 二、Spring MVC 自定义拦截器实现 对于 Spring MVC 框架中的请求拦截需求,通常会继承 `HandlerInterceptor` 并配合 `WebMvcConfigurer` 进行配置。 ##### 1. 创建拦截器类 ```java package com.example.springmvc.interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Request is being processed by custom interceptor..."); // 如果返回 false,则中断后续流程;如果返回 true,则继续执行控制器逻辑 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("Post-processing after controller logic executed but before rendering view."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("Final processing after entire request has been completed."); } } ``` ##### 2. 配置拦截器拦截器应用到特定路径上: ```java package com.example.config; import com.example.springmvc.interceptor.CustomInterceptor; import org.springframework.beans.factory.annotation.Autowired; 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 WebConfig implements WebMvcConfigurer { private final CustomInterceptor customInterceptor; @Autowired public WebConfig(CustomInterceptor customInterceptor) { this.customInterceptor = customInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(customInterceptor).addPathPatterns("/api/**"); } } ``` 以上代码展示了如何针对 `/api/` 路径下的所有请求启用自定义拦截器[^4]。 --- #### 三、Redis 基础上的限流防刷拦截器 当涉及到高并发场景时,可以结合 Redis 来设计一种限流机制,并集成到拦截器中。 ##### 1. 使用 Redis 记录访问频率 假设我们希望限制某个 API 在每分钟内的最大访问次数为 100 次: ```java package com.example.redis.interceptor; import redis.clients.jedis.Jedis; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class RateLimitingInterceptor implements HandlerInterceptor { private static final int MAX_REQUESTS_PER_MINUTE = 100; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Jedis jedis = new Jedis("localhost"); String key = "rate_limit:" + request.getRequestURI(); Long count = jedis.incr(key); if (count == 1) { jedis.expire(key, 60); // 设置过期时间为 60 秒 } if (count > MAX_REQUESTS_PER_MINUTE) { response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); response.getWriter().write("Too many requests!"); return false; // 终止请求 } return true; } } ``` 此代码利用 Redis 的原子计数功能记录每个 URI 的访问频次,并在超出阈值时拒绝服务[^5]。 --- ### 总结 无论是基于 MyBatis 的 SQL 执行拦截还是 Spring MVC 请求级别的拦截,都可以通过扩展相应的接口并覆盖核心方法来自定义行为。而引入外部工具(如 Redis),则能够进一步增强系统的功能性与安全性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值