一个请求在Spring中处理流程是有多种方式拦截处理的,而且,请求是可以拆分为进入和响应2个操作的,进入我们通常会对请求参数做处理,而响应我们通常会对响应参数做处理,Spring提供了多种方式给开发者。
一、HandlerInterceptor
我们写的controller,在Spring中被定义为handler,拦截controller的拦截器被定义为org.springframework.web.servlet.HandlerInterceptor。
拦截器的拦截逻辑是在org.springframework.web.servlet.DispatcherServlet中写的,需要注意的是,如果入口拦截顺序是a->b->c的话,那么出口拦截顺序是c->b->a,这个逻辑可以看org.springframework.web.servlet.HandlerExecutionChain里的一段逻辑。
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 = 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;}}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 afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
这里能很清晰的看到循环使用的次序。
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;}this.interceptorIndex = i;}return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}
org.springframework.web.servlet.HandlerInterceptor
public interface HandlerInterceptor {/*** Interception point before the execution of a handler. Called after* HandlerMapping determined an appropriate handler object, but before* HandlerAdapter invokes the handler.* <p>DispatcherServlet processes a handler in an execution chain, consisting* of any number of interceptors, with the handler itself at the end.* With this method, each interceptor can decide to abort the execution chain,* typically sending an HTTP error or writing a custom response.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation returns {@code true}.* @param request current HTTP request* @param response current HTTP response* @param handler chosen handler to execute, for type and/or instance evaluation* @return {@code true} if the execution chain should proceed with the* next interceptor or the handler itself. Else, DispatcherServlet assumes* that this interceptor has already dealt with the response itself.* @throws Exception in case of errors*/default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/*** Interception point after successful execution of a handler.* Called after HandlerAdapter actually invoked the handler, but before the* DispatcherServlet renders the view. Can expose additional model objects* to the view via the given ModelAndView.* <p>DispatcherServlet processes a handler in an execution chain, consisting* of any number of interceptors, with the handler itself at the end.* With this method, each interceptor can post-process an execution,* getting applied in inverse order of the execution chain.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation is empty.* @param request current HTTP request* @param response current HTTP response* @param handler the handler (or {@link HandlerMethod}) that started asynchronous* execution, for type and/or instance examination* @param modelAndView the {@code ModelAndView} that the handler returned* (can also be {@code null})* @throws Exception in case of errors*/default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}/*** Callback after completion of request processing, that is, after rendering* the view. Will be called on any outcome of handler execution, thus allows* for proper resource cleanup.* <p>Note: Will only be called if this interceptor's {@code preHandle}* method has successfully completed and returned {@code true}!* <p>As with the {@code postHandle} method, the method will be invoked on each* interceptor in the chain in reverse order, so the first interceptor will be* the last to be invoked.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation is empty.* @param request current HTTP request* @param response current HTTP response* @param handler the handler (or {@link HandlerMethod}) that started asynchronous* execution, for type and/or instance examination* @param ex any exception thrown on handler execution, if any; this does not* include exceptions that have been handled through an exception resolver* @throws Exception in case of errors*/default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}}
二、HandlerMethodArgumentResolver
上面的HandlerInterceptor可以清楚的看到接收的参数是HttpServletRequest,这是最早期的参数,紧接着Spring会从HttpServletRequest里把参数读取到controller定义的请求参数里面,此时用到的类型是HttpMessageConverter,他把参数写入controller中,此时是可以在参数写入前后做一些操作的。
三、RequestBodyAdvice
org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
这2个可以直接修改请求参数,可以看到写入之后,就得到了controller定义的参数类型。
public interface RequestBodyAdvice {/*** Invoked first to determine if this interceptor applies.* @param methodParameter the method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the selected converter type* @return whether this interceptor should be invoked or not*/boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked second before the request body is read and converted.* @param inputMessage the request* @param parameter the target method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the converter used to deserialize the body* @return the input request or a new instance (never {@code null})*/HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;/*** Invoked third (and last) after the request body is converted to an Object.* @param body set to the converter Object before the first advice is called* @param inputMessage the request* @param parameter the target method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the converter used to deserialize the body* @return the same body or a new instance*/Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked second (and last) if the body is empty.* @param body usually set to {@code null} before the first advice is called* @param inputMessage the request* @param parameter the method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the selected converter type* @return the value to use, or {@code null} which may then raise an* {@code HttpMessageNotReadableException} if the argument is required*/@NullableObject handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);}
public interface ResponseBodyAdvice<T> {/*** Whether this component supports the given controller method return type* and the selected {@code HttpMessageConverter} type.* @param returnType the return type* @param converterType the selected converter type* @return {@code true} if {@link #beforeBodyWrite} should be invoked;* {@code false} otherwise*/boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked after an {@code HttpMessageConverter} is selected and just before* its write method is invoked.* @param body the body to be written* @param returnType the return type of the controller method* @param selectedContentType the content type selected through content negotiation* @param selectedConverterType the converter type selected to write to the response* @param request the current request* @param response the current response* @return the body that was passed in or a modified (possibly new) instance*/@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response);}
四、整体时序
HandlerInterceptor preHandle ->
RequestBodyAdvice ->
HandlerMethodArgumentResolver ->
RequestBodyAdvice ->
controller ->
AOP afterReturning ->
ResponseBodyAdvice beforeBodyWrite ->
HttpMessageConverter(转JSON )->
HandlerInterceptor postHandle ->
HandlerInterceptor afterCompletion