spring拦截器机制及其使用

您所在的位置:网站首页 springboot拦截器posthandle spring拦截器机制及其使用

spring拦截器机制及其使用

2023-07-07 16:24| 来源: 网络整理| 查看: 265

1、spring拦截器介绍

spring针对处理器映射器提供了一种拦截器的机制,允许我们自定义一些处理逻辑,比如打印日志、校验用户是否登录等,然后spring会在调用最终的处理器前后执行我们自定义的逻辑。

注:

(1)处理器映射器,也叫HandlerMapping,关于它的作用,可以简单理解为它能根据请求路径找到我们在controller中写的能处理外部请求的方法。

(2)本文所展示的代码基于springboot 2.7.0版本(对应spring 5.3.20版本)

1.1、 拦截器的结构

spring要求我们自定义的拦截器需要实现HandlerInterceptor接口,我们先看下HandlerInterceptor接口内容:

public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } } 复制代码

简要解释下上面三个方法:

preHandle方法

在目标处理器(controller接口方法)运行之前执行,返回boolean值

postHandle方法

在目标处理器(controller接口方法)运行之后执行

afterCompletion方法

在整个请求完成(页面渲染)之后执行

1.2、拦截器原理

先为大家展示一下spring执行拦截器调用相关的代码,重点就在DispatcherServlet类中

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 第一步,找到请求对应的处理器,并生成处理器链,里面包括处理器信息以及拦截器列表信息 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 第二步,通过处理器链调用各个拦截器的preHandler方法,如果返回了false,说明不能执行目标处理器 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 第三步,执行目标处理器方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); // 第四步,通过处理器链(倒序)调用各个拦截器的postHandler方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 处理目标处理器返回的数据(进行页面渲染),并倒序调用各个拦截器的afterCompletion方法 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { // 出现异常时,执行本类中triggerAfterCompletion方法,进而调用mappedHandler.triggerAfterCompletion triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } } private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception { if (mappedHandler != null) { // 只要处理器链对象不为空,就直接去调用处理器链里面的拦截器列表中已执行拦截器的afterCompletion方法 mappedHandler.triggerAfterCompletion(request, response, ex); } throw ex; } 复制代码

上面代码块中提到的mappedHandler的applyPreHandle、applyPostHandle和triggerAfterCompletion方法位于HandlerExecutionChain类,实现如下:

HandlerExecutionChain类 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } // 此下标记录了当前拦截器执行到了哪一个,方便倒序执行拦截器的afterCompletion方法 this.interceptorIndex = i; } return true; } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) { // applyPreHandle方法记录了已经执行了preHandle方法的拦截器在拦截器列表中的下标,也即interceptorIndex的值 for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { // 倒序执行所有拦截器的postHandle方法 for (int i = this.interceptorList.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } } 复制代码

简单总结下spring拦截器的原理:

(1)DispatcherServlet根据当前的请求找到执行器链(内部封装了目标处理器,以及目标处理器拥有的拦截器列表)

(2)首先会顺序执行拦截器链中的各个拦截器的preHandle方法,这时候会有两种情况

每个拦截器的preHandle都返回了true,所有处理器的preHandle都会得到执行,然后进入第3步

如果执行到某个拦截器的preHandle方法返回了false,就会倒序执行所有已经执行过的拦截器的afterCompletion方法,并且请求会直接结束,不会再执行后续的操作

(3)所有的拦截器都返回了true,则执行目标方法(接口)

(4)接着就是倒序执行所有拦截器的postHandle方法

(5)然后就是渲染页面,并倒序执行所有拦截器的afterCompletion方法

(6)当上面的第2步和第5步之间的任何一步出了异常,也都会倒序执行所有拦截器的afterCompletion方法

2、拦截器的使用

我们可以自定义一个拦截器,针对未登录用户的请求进行认证拦截。

2.1、定义拦截器

实现思路:

定义LoginInterceptor拦截器,实现HandlerInterceptor接口。 正常情况下,用户登录成功后,会将用户信息保存到session中,如果通过用户的请求,无法在session中查询到用户信息,说明用户未登录。 @Slf4j @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); UserDTO user = (UserDTO) session.getAttribute("USER"); //session没有用户信息,说明用户未登录,阻止继续处理用户的请求,直接返回false if(user == null) { log.warn("用户未登录,不予处理请求"); return false; } return true; } } 复制代码

UserDTO的信息如下:

public class UserDTO { private String name; private String userId; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } 复制代码 2.2、注册拦截器到容器

定义WebMvcConfig配置类,实现WebMvcConfigurer接口

@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); } } 复制代码 结束语

本文先分享到这里,觉得有收获的朋友,可以关注我,或者进行分享或收藏,有疑惑的也可以来私聊评论,我会及时进行回复~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3