SpringMVC之拦截器
2021/11/16 23:40:16
本文主要是介绍SpringMVC之拦截器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
拦截器
目录- 拦截器
- 1、简单介绍拦截器
- 1.1、拦截器接口
- 2、举例说明
- 2.1、applyPreHandle方法
- 2.2、triggerAfterCompletion方法
- 2.3、applyPostHandle方法
- 2.4、triggerAfterCompletion方法
- 3、总结
- 1、简单介绍拦截器
1、简单介绍拦截器
利用spring的AOP思想建立起来的,与之类似的就是web中的filter。这里介绍一下目标方法,目标方法是我们在controller中书写的方法。
拦截器是在目标方法执行之前执行的。
在springmvc中,要想对springmvc进行定制化,那么首先应该实现接口WebMvcConfigurer,然后去重写其中的方法,最终将该配置类添加到容器中去。
1.1、拦截器接口
接口中有三个默认方法,子类可以有选择性的去实现接口中的方法:
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 { } }
2、举例说明
下面举一个例子来进行说明:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerIntecepter()) // 拦截哪些请求 .addPathPatterns("/**") // 放过哪些请求 .excludePathPatterns("/login","/"); } }
然后写一个类来继承HandlerInterceptor,并重写其中的方法:
@Slf4j public class LoginHandlerIntecepter implements HandlerInterceptor { /** * 目标方法执行之前 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); String method = request.getMethod(); log.info("请求进来的requestURI是:{},对应的方式是:{}",requestURI,method); if ("/user/login".equals(requestURI)){ log.info("符合规则,开始来进入....................."); return true; } return false; } /** * 目标方法执行之后 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("进入到postHandler方法中来..............."); } /** * 视图渲染之后,返回结果之前 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("进入到afterCompletion方法中来..............."); } }
controller中的目标方法:
@RestController @RequestMapping("user") @Slf4j public class UserLoginController { @GetMapping("login") public String hello(){ log.info("进入到目标方法中来............"); Map<String,String> map = new HashMap<>(); map.put("k1","k2"); map.put("k3","k4"); return map.toString(); } }
最终控制台打印出来对应的结果:
{k1=k2, k3=k4}
那么来探索一下其中的原理。
首先进入到DispatcherServlet类中来,看到大致的代码结构:
// Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); .................. if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
2.1、applyPreHandle方法
那么就看下这段在执行目标方法之前的判断:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { // 首先获取得到所有的拦截器(包括我们自己写的拦截器) HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // 按照顺序开始来进行遍历。questionOne for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; // 如果某个拦截器的preHandle方法执行成功,返回为true。在if方法中就是false // 但是如果拦截器的preHandle方法返回为false,那么if判断中是false,会立马执行triggerAfterCompletion // QuestionTwo if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } // 每个拦截器执行完preHandle是true之后,将会执行到这里来。然后记录一下是哪个拦截器执行成功 // 如果拦截器执行为false,那么将会立即返回。也就是说这里的下标只会记录到执行完成的拦截器 this.interceptorIndex = i; } } return true; }
QuestionOne:如果有多个拦截器,会按照加载的顺序来执行,如果有数组中的顺序是【A、B、C】,那么执行顺序就是A、B、C
QuestionTwo:只要有一个拦截器的preHandle方法没有执行成功,那么就立即执行这个拦截器的triggerAfterCompletion方法
2.2、triggerAfterCompletion方法
那么看一下这个triggerAfterCompletion方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { // 获取得到所有的拦截器 HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // 从这里可以看到和上面的区别。上面是正序,这里是倒序 // 这里会用索引来进行记录已经执行成功了的拦截器 // QuestionOne for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { // 开始执行其afterCompletion方法 interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
QuestionOne:如果有多个拦截器,会按照加载的顺序来执行,如果有数组中的顺序是【A、B、C】,那么执行顺序就是A、B、C
这里默认的是所有的拦截器中的方法执行都是正常的。
那么下面来介绍一下不正常的情况:如果数组中的顺序是【A、B、C】,如果拦截器A和B执行preHandle都是成功的,而C的执行是不成功的,那么将会立即走到B的方法,而不会继续走到C的后续方法中去。
那么继续看DispatcherServlet中的方法:
// Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); .................. if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 目标方法执行完成调用的方法 mappedHandler.applyPostHandle(processedRequest, response, mv);
那么跟进一下这里的方法:
2.3、applyPostHandle方法
/** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
这段代码就不在来做细致分析了,这里很显然和afterCompletion方法是一样的。如果数组中的顺序是【A、B、C】,那么applyPostHandle的执行顺序就是C、B、A(因为在applyPostHandle)方法中返回值是void,只是简单的执行而已,不做其他的操作。
2.4、triggerAfterCompletion方法
最后再来看一段代码:
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); } } }
这里也即是说明了,无论拦截器的triggerAfterCompletion执行是哪一步出现了问题,最终都将会执行这里的方法
3、总结
最终以一幅图来总结:
当所有的拦截器preHandler都执行成功的时候:
当有其中一个拦截器的preHandler方法为false的时候:
因为源码中写的是:
if (!mappedHandler.applyPreHandle(processedRequest, response)) { // 代码执行到了这里,那么只能说明上面的是为false return; } // 而这个方法为false的原因就在于其中有一个preHandle没有返回为true boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
所以结束掉整个请求
这篇关于SpringMVC之拦截器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南