什么是拦截器?拦截器如何配置?

拦截器

1. 什么是拦截器?2. 拦截器的实现2.1 定义拦截器2.2 注册配置拦截器

3. 拦截器的工作原理3.1 执行流程3.2 源码分析

1. 什么是拦截器?

拦截器(Interceptor)是 Spring MVC 中的一个很重要的组件,主要用于拦截用户的请求,可以在指定方法前后, 根据业务需要执行预先设定的代码。它的作用主要有:

权限验证:验证用户是否登录、是否有权限访问某个接口。日志记录:记录请求信息的日志,如请求参数,响应信息等。性能监控:监控系统的运行性能,如慢查询接口等。通用行为:插入一些通用的行为,比如开发环境忽略某些请求。

也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在用户的请求响应前后执行。也可以在用户请求前阻止其执行。在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通用性的操作, 比如通过拦截器来拦截前端发来的请求, 判断Session中是否有登录用户的信息。如果有就可以放行, 如果没有就进行拦截。

2. 拦截器的实现

2.1 定义拦截器

自定义一个拦截器非常简单,只需要实现HandlerInterceptor这个接口即可,并重写该接口的方法。该接口有三个可以实现的方法:

preHandle():该方法会在目标方法执行前执行,当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类Controller中的方法执行等 )。postHandle(): 该方法会在目标方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图作出进一步的修改。afterCompletion():该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

@Slf4j

public class LoginInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

log.info("LoginInterceptor 目标方法执行前执行...");

// true-放行 false-拦截

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

log.info("LoginInterceptor 目标方法执行后执行...");

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

log.info("LoginInterceptor 视图渲染完毕之后执行,最后执行....");

}

}

2.2 注册配置拦截器

想要在Spring Boot生效,只需要定义一个配置类,实现WebMvcConfigurer这个接口,并且实现其中的addInterceptiors()方法即可。

public class WebConfig implements WebMvcConfigurer {

// 注入自定义的拦截器对象

@Autowired

private LoginInterceptor loginInterceptor;

@Override

public void addInterceptors(InterceptorRegistry registry) {

// 注册自定义的拦截器对象

registry.addInterceptor(loginInterceptor)

.addPathPatterns("/**")// 设置拦截器拦截的请求路径(/** 表示拦截所有请求)

.excludePathPatterns("/user/login");//设置拦截器 排除拦截的路径

}

}

我们试试启动服务,访问任意一个请求,观察后端日志: 可以看到preHandle 方法执行之后就放行了, 开始执⾏目标方法, 目标⽅法执⾏完成之后执⾏postHandle和afterCompletion⽅法。 我们把拦截器中preHandle⽅法的返回值改为false, 再观察运⾏结果: 可以看到, 拦截器拦截了请求, 没有进⾏响应。

3. 拦截器的工作原理

3.1 执行流程

没有拦截器时,程序的调用顺序: 有了拦截器之后,程序会在调用Controller 之前进行相应的业务处理: 注意:

添加拦截器后, 程序在执⾏Controller的⽅法之前, 请求会先被拦截器拦截住。执⾏ preHandle() ⽅法,这个⽅法需要返回⼀个布尔类型的值。 如果返回true, 就表示放⾏本次操作, 继续访问Controller中的⽅法;如果返回false,则不会放⾏(Controller中的⽅法也不会执⾏)。Controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据。

3.2 源码分析

Spring Boot 拦截器是基于 Java 的 Servlet 规范实现的,通过实现 HandlerInterceptor 接口来实现拦截器功能。 当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet,它来控制程序的执行顺序。所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法。如果有拦截器, 会先执⾏拦截器preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法。Controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 和 afterCompletion() ,返回给DispatcherServlet,最终给浏览器响应数据。 DispatcherServlet 接收到请求后, 执⾏doDispatch 调度⽅法, 再将请求转给Controller。 doDispatch 源码实现如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {

try {

ModelAndView mv = null;

Exception dispatchException = null;

try {

processedRequest = this.checkMultipart(request);

multipartRequestParsed = processedRequest != request;

//1. 获取执⾏链

//遍历所有的 HandlerMapping 找到与请求对应的Handler

mappedHandler = this.getHandler(processedRequest);

if (mappedHandler == null) {

this.noHandlerFound(processedRequest, response);

return;

}

//2. 获取适配器

//遍历所有的 HandlerAdapter,找到可以处理该 Handler 的HandlerAdapter

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

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;

}

}

//3. 执⾏拦截器preHandle⽅法

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

//4. 执⾏⽬标⽅法

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

this.applyDefaultViewName(processedRequest, mv);

//5. 执⾏拦截器postHandle⽅法

mappedHandler.applyPostHandle(processedRequest, response, mv);

} catch (Exception var20) {

dispatchException = var20;

} catch (Throwable var21) {

dispatchException = new NestedServletException("Handler dispatch failed", var21);

}

//6. 处理视图, 处理之后执⾏拦截器afterCompletion⽅法

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

} catch (Exception var22) {

//7. 执⾏拦截器afterCompletion⽅法

this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);

} catch (Throwable var23) {

this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));

}

} finally {

if (asyncManager.isConcurrentHandlingStarted()) {

if (mappedHandler != null) {

mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

}

} else if (multipartRequestParsed) {

this.cleanupMultipart(processedRequest);

}

}

}

从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,⽽applyPreHandle ⽅法的实现源码如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {

// 获取项⽬中使⽤的拦截器 HandlerInterceptor

HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);

if (!interceptor.preHandle(request, response, this.handler)) {

this.triggerAfterCompletion(request, response, (Exception)null);

return false;

}

}

return true;

}

在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor , 并执⾏拦截器中的preHandle ⽅法,这样就会咱们前⾯定义的拦截器对应上了,如下图所示: 如果拦截器返回true, 整个发放就返回true, 继续执⾏后续逻辑处理;如果拦截器返回fasle, 则中断后续操作。 因此,可以得出结论,拦截器的实现主要是依赖 Servlet 或 Spring 执行流程来进行拦截和功能增强的。

[an error occurred while processing the directive]
Copyright © 2088 世界杯决赛结果_世界杯队伍 - yzxygq.com All Rights Reserved.
友情链接