ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringMVC之拦截器

2021-11-16 23:34:16  阅读:149  来源: 互联网

标签:拦截器 SpringMVC interceptors request handler 方法 response


拦截器

目录

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,interceptors,request,handler,方法,response
来源: https://www.cnblogs.com/likeguang/p/15564413.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有