Spring Boot拦截器使用

本文详细介绍了SpringMVC中的拦截器(Interceptor)概念及其工作原理。重点讲解了HandlerInterceptor的使用方法,包括preHandle、postHandle及afterCompletion三个核心方法的作用与应用场景。此外,还提供了在SpringBoot项目中配置拦截器的具体示例。

在Web开发中,拦截器(Interceptor)可以用来验证是否登录、预先设置数据以及统计方法的执行效率等。


Spring中的拦截器分两种,一是HandlerInterceptor,另一个是MethodInterceptor。这里主要说以下HandlerInterceptor。


HandlerInterceptor是SpringMVC项目中的拦截器,拦截目标是请求的地址,比MethodInterceptor先执行。实现一个HandlerInterceptor拦截器可以直接实现该接口,也可以继承HandlerInterceptorAdapter类。


SpringMVC处理请求的整个过程是先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor拦截器;然后在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。


下面详细介绍一下需要Override的三种方法:

(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式调用的,在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。


   (2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。


   (3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。


在Spring Boot中配置拦截器,需要写一个配置类继承WebMvcConfigurerAdapter类并添加该拦截器。

@Component
public class XdtoutiaoWebConfiguration extends WebMvcConfigurerAdapter {
    @Autowired
    PassportInterceptor passportInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(passportInterceptor);
        super.addInterceptors(registry);
    }
}




参考资料:

http://blog.youkuaiyun.com/hongxingxiaonan/article/details/48090075

http://www.cnblogs.com/yadongliang/p/7800775.html


<think>我们正在讨论Spring Boot拦截器使用方法。根据引用内容,拦截器Spring Boot中用于在请求处理之前和之后插入自定义逻辑。以下是关键步骤和注意事项: 1. **创建拦截器类**:实现`HandlerInterceptor`接口,重写`preHandle`、`postHandle`和`afterCompletion`方法。 2. **注册拦截器**:通过配置类实现`WebMvcConfigurer`接口,并重写`addInterceptors`方法,将拦截器添加到拦截器注册表中。 3. **注意点**:在拦截器中无法直接使用`@Value`注入属性,因为拦截器是由Spring容器管理的,但属性注入需要在Spring上下文初始化完成后才能进行,而拦截器在初始化时可能尚未完成属性注入。 根据引用[2],在配置类中直接`new`拦截器实例会导致拦截器中的`@Value`失效。解决方法是使用Spring的依赖注入来获取拦截器实例。 另外,引用[3]提到在拦截器中获取请求体时,需要将请求包装为`ContentCachingRequestWrapper`,以便多次读取请求体。 下面将详细说明使用方法: ### 一、创建拦截器 实现`HandlerInterceptor`接口的三个方法: - `preHandle`:在请求处理之前执行(Controller方法调用之前) - `postHandle`:在请求处理之后执行(Controller方法调用之后,视图渲染之前) - `afterCompletion`:在整个请求结束之后执行(视图渲染完毕) 示例拦截器: ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在Controller方法执行之前调用 // 返回true则继续执行,返回false则中断 System.out.println("Pre Handle method is Calling"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 在Controller方法执行之后,视图渲染之前调用 System.out.println("Post Handle method is Calling"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在整个请求结束之后调用(视图渲染完毕) System.out.println("After Completion method is Calling"); } } ``` ### 二、注册拦截器(正确方式) 为了解决拦截器中`@Value`失效的问题,我们不应该在配置类中直接`new`拦截器,而是将拦截器声明为Spring Bean,然后在配置类中注入这个Bean。 1. **将拦截器加入Spring容器**:在拦截器类上使用`@Component`注解。 2. **在配置类中注入拦截器**:使用`@Autowired`注入拦截器实例。 修改后的拦截器(添加`@Component`): ```java import org.springframework.stereotype.Component; @Component // 添加此注解 public class MyInterceptor implements HandlerInterceptor { // ... 方法实现同上 } ``` 配置类: ```java 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 InterceptorConfig implements WebMvcConfigurer { // 自动注入拦截器Bean @Autowired private MyInterceptor myInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 注册拦截器,并指定拦截路径和排除路径 registry.addInterceptor(myInterceptor) .addPathPatterns("/api/**") // 拦截/api/开头的所有请求 .excludePathPatterns("/api/public/**"); // 排除/api/public/下的请求 } } ``` ### 三、在拦截器使用@Value(可选) 如果需要在拦截器使用`@Value`注入配置值,可以在拦截器类中直接使用,因为此时拦截器已经是Spring Bean。 示例: ```java @Component public class MyInterceptor implements HandlerInterceptor { @Value("${app.auth.token}") // 注入配置属性 private String authToken; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 使用注入的authToken String token = request.getHeader("Authorization"); if (!authToken.equals(token)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); return false; } return true; } } ``` ### 四、读取请求体(引用[3]中的最佳实践) 由于HttpServletRequest的输入流只能读取一次,如果需要多次读取(例如在拦截器和Controller中都读取),需要将请求包装为`ContentCachingRequestWrapper`。 在拦截器中读取请求体: ```java @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 将请求包装为ContentCachingRequestWrapper ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); // 读取请求体 byte[] body = wrappedRequest.getContentAsByteArray(); String requestBody = new String(body, StandardCharsets.UTF_8); // 使用requestBody // ... // 注意:将包装后的请求继续传递 return true; } ``` 但是,在配置拦截器之前,需要确保请求被包装。通常,我们可以通过一个过滤器来实现请求的包装,然后在拦截器中就可以使用包装后的请求。 ### 五、拦截器执行顺序 如果需要多个拦截器,可以通过`order`方法设置执行顺序(值越小越先执行): ```java registry.addInterceptor(myInterceptor1).order(1); registry.addInterceptor(myInterceptor2).order(2); ``` ### 六、拦截器最佳实践(参考引用[3]) - **职责单一**:每个拦截器只处理一个功能(如认证、日志、参数校验)。 - **性能考虑**:避免在拦截器中执行耗时操作(如数据库查询),必要时使用缓存。 - **合理排序**:将认证拦截器放在最前面,以便尽早拦截非法请求。 - **异常处理**:在拦截器中抛出的异常会被Spring的异常处理器处理,但注意不要泄露敏感信息。 - **避免循环**:拦截器的重定向逻辑可能导致循环,应设置终止条件。 ### 七、总结 1. 创建拦截器:实现`HandlerInterceptor`接口并用`@Component`标记。 2. 注册拦截器:通过`WebMvcConfigurer`配置类注入拦截器并注册,设置拦截路径。 3. 属性注入:在拦截器类中可以直接使用`@Value`注入配置属性。 4. 请求体读取:使用`ContentCachingRequestWrapper`包装请求以多次读取请求体。 §§ 相关问题 1. Spring Boot拦截器和过滤器有什么区别? 2. 如何在拦截器中获取请求参数? 3. 拦截器执行顺序如何控制? 4. 拦截器中如何实现用户认证? </think> ### Spring Boot 拦截器使用指南 #### 一、拦截器核心概念 拦截器(Interceptor)在 Spring Boot 中用于**拦截 HTTP 请求**,在 Controller 方法执行前后插入自定义逻辑。与过滤器的区别在于: - 拦截器基于 Spring MVC 框架,可访问处理程序对象 - 执行时机:`请求到达 Controller 前 → Controller 处理后 → 视图渲染后`[^1] #### 二、实现步骤 ##### 1. 创建拦截器类 实现 `HandlerInterceptor` 接口: ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler ) throws Exception { // 在Controller执行前调用 System.out.println("PreHandle: 验证Token"); String token = request.getHeader("Authorization"); if(!"valid_token".equals(token)) { response.sendError(401, "Invalid token"); return false; // 中断请求 } return true; // 继续执行 } @Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView ) throws Exception { // Controller执行后,视图渲染前调用 System.out.println("PostHandle: 添加响应头"); response.addHeader("X-Processed-By", "AuthInterceptor"); } @Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex ) throws Exception { // 整个请求完成后调用(资源清理) System.out.println("Request completed"); } } ``` ##### 2. 注册拦截器(解决@Value失效问题) 通过 `WebMvcConfigurer` 注入 Spring Bean 而非直接 `new` 实例[^2]: ```java 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 { // 注入Spring管理的拦截器Bean @Autowired private AuthInterceptor authInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/api/**") // 拦截路径 .excludePathPatterns("/api/public/**"); // 排除路径 } } ``` #### 三、关键功能实现 ##### 1. 获取请求体内容 使用 `ContentCachingRequestWrapper` 包装请求[^3]: ```java public boolean preHandle(HttpServletRequest request, ...) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); byte[] body = wrappedRequest.getContentAsByteArray(); String requestBody = new String(body, StandardCharsets.UTF_8); System.out.println("Request Body: " + requestBody); return true; } ``` ##### 2. 多拦截器执行顺序 ```java registry.addInterceptor(new LogInterceptor()).order(1); registry.addInterceptor(new AuthInterceptor()).order(2); // order值越小优先级越高 ``` #### 四、最佳实践 1. **职责单一化** 每个拦截器只处理一个关注点(如认证/日志/限流) 2. **性能优化** ```java // 避免在preHandle中执行数据库操作 if (!shouldProcess(request)) { return true; // 快速放行 } ``` 3. **异常处理** 在 `afterCompletion` 中统一处理异常: ```java public void afterCompletion(... Exception ex) { if (ex != null) { // 记录异常日志 } } ``` 4. **安全实践** 敏感操作拦截器应放在最前: ```mermaid graph LR A[请求] --> B(认证拦截器) B --> C{认证通过?} C -->|是| D[日志拦截器] C -->|否| E[返回401] D --> F[业务Controller] ``` #### 五、常见问题解决 1. **@Value 失效问题** 原因:直接 `new Interceptor()` 导致 Spring 属性注入失效 方案:通过 `@Autowired` 注入拦截器(见第二章) 2. **重复读取 Request Body** 方案:使用 `ContentCachingRequestWrapper` 包装请求 3. **拦截静态资源** 方案:排除静态资源路径 ```java .excludePathPatterns("/css/**", "/js/**") ``` > 完整示例代码:[GitHub 示例项目](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-samples) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值