SpringMVC中的拦截器

一、拦截器

SpringMVC也可以使用拦截器请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口

在这里插入图片描述

  • preHandle目标方法运行之前调用
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}
  • postHandle目标方法运行之后调用,目标方法一调用就会执行
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
  • afterCompletion在请求整个完成之后,来到目标页面之后,资源响应之后才执行
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

二、单拦截器运行流程

请求处理目标方法

@Controller
public class InterceptorTestController {

    @RequestMapping("/testInterceptor")
    public String testInterceptor(){
		System.out.println("执行目标方法");
        return "success";
    }
}

创建一个拦截器类实现HandlerInterceptor接口

public class MyFirstInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyFirstInterceptor...preHandle()执行");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyFirstInterceptor...postHandle()执行");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyFirstInterceptor...afterCompletion()执行");
    }
}

在SpringMVC配置文件中注册这个拦截器的工作,配置这个拦截器来拦截哪些请求的目标方法

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--配置某个拦截器,默认拦截所有请求-->
        <bean class="controller.MyFirstInterceptor"></bean>
        <!--
        &lt;!&ndash;配置某个拦截器更详细的信息&ndash;&gt;
        
        <mvc:interceptor>
            &lt;!&ndash;只拦截指定的请求&ndash;&gt;
            <mvc:mapping path="/testInterceptor"/>
            <bean class="controller.MyFirstInterceptor"></bean>
        </mvc:interceptor>
        
        -->
    </mvc:interceptors>

执行流程的打印结果:
在这里插入图片描述
正常流程:拦截器的preHandler—>目标方法—>拦截器的postHandler—>页面—>拦截器的afterCompletion

注意,如果preHandler不放行,就没有之后的流程。但是如果执行目标方法执行了,不管报没报错,afterCompletion都会执行,而如果报错,postHandler不会执行。

三、多拦截器的运行流程

配置多个拦截器

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--配置某个拦截器,默认拦截所有请求-->
        <bean class="controller.MyFirstInterceptor"></bean>
        <!--配置某个拦截器更详细的信息-->
        <mvc:interceptor>
            <!--只拦截指定的请求-->
            <mvc:mapping path="/testInterceptor"/>
            <bean class="controller.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

正常流程,多个拦截器同时工作,执行流程如下:
在这里插入图片描述
如果出现异常,只要其中任何一个环节出现异常,后续的方法都不会执行。

但是如果MySecondInterceptorpreHandler出现异常,执行流程如下:
在这里插入图片描述
即使MySecondInterceptorpreHandler不放行,MyFirstInterceptor还是会执行它的afterCompletion。也就是MySecondInterceptor之前已经放行了的其它拦截器还是会执行各自的afterCompletion

四、拦截器执行流程源码分析

	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);

				// Determine handler for the current 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 = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				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);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			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);
				}
			}
		}
	}

mappedHandler = getHandler(processedRequest)方法执行后,mappedHandler获取的是HandlerExecutionChain,也就是说获取的不只有目标方法,还有所有的拦截器。

applyPreHandler()先是执行所有拦截器的preHandler,循环遍历每一个拦截器,如果preHandler不放行,就会触发执行AfterCompletion()方法,然后再返回false;如果preHandler放行,就会用索引记录一下已放行的拦截器。只要有一个拦截器返回false,目标方法以后的方法都不会执行,直接跳到afterCompletion()方法。

注意interceptorIndex索引值初始化为-1,因为在triggerAfterCompletion()方法中,会倒序遍历每个拦截器的afterCompletion()方法。所以如果不是初始化为-1,在没有执行preHandler()方法的情况下,也会执行一遍多余的afterCompletion()方法。

	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;
	}

然后执行ha.handle()方法,适配器执行目标方法

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

如果目标方法正常执行,就会执行applyPostHandle()

mappedHandler.applyPostHandle(processedRequest, response, mv);

applyPostHandle()的循环遍历是倒序遍历

	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);
			}
		}
	}

无论目标方法是否正常执行都将会执行页面渲染方法processDispatchResult()

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

如果render(mv, request, response)页面渲染正常执行完成,渲染完成以后,就会执行afterCompletion()方法。

如果页面渲染执行也有异常doDispatch()方法中会捕捉到异常,异常处理也会执行afterCompletion()方法

		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}

所以afterCompletion()方法不管怎样都会执行。

triggerAfterCompletion()方法能获取到已放行拦截器的所有索引,通过倒序遍历执行所有已放行拦截器的afterCompletion()方法

	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}

综上所述:

单拦截器执行顺序
在这里插入图片描述
多拦截器执行顺序
在这里插入图片描述
在这里插入图片描述

五、Filter过滤器与拦截器

对于拦截器,它是放在容器中的,需要配置。如果需要某些功能,可以装配其它组件配合完成。如果过滤请求处理非常复杂,就可以使用拦截器。拦截器脱离了SpringMVC就没有用了。

对于Filter过滤器,它是JavaWeb的三大组件,但是不能像拦截器一样自动装配一些其它组件,Filter是交给Tomcat管理的,服务器一启动就会创建Filter对象,但是它不能加载到IOC容器中,也就无法从容器中获取其它组件。如果过滤请求处理比较简单,就可以使用Filter。Filter脱离SpringMVC还是能用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值