本文分析基于 基于spring-webmvc 4.0.3.RELEASE
springMVC 核心servlet —DispatcherServlet
整体结构
Aware:如果某个类想使用Spring的一些东西,就可以实现Aware接口
Capable:具有spring某个类的能力
时序图预览
创建过程
HttpServletBean的创建
实现了EnvironmentCapable
、EnvironmentAware
,主要作用是将Servlet中配置的参数设置到相应的容器
FramewordkServlet的创建
实现了ApplicationContextAware
,其主要作用是调用initWebApplicationContext()
初始化WebApplicationContext
- 获取spring的根工期rootContext
- 设置webApplicationContext并根据情况调用onRefresh方法
- 将WebApplicationContext设置到ServletContext中
DispatcherServlet的创建
onRefresh方法是DispatcherServlet的入口方法,onRefresh中调用了initStrategies()方法来初始化一些策略组件
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
如果没有配置相关,会使用默认的配置,在DispatcherServlet.properties文件中
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
Tips:If no bean is defined with the given name in the BeanFactory for this namespace, no multipart handling is provided.
组件概览
HandlerMapping
它的作用是根据request找到对应的处理器handler和Interceptors ,首先看下接口信息
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
/**
* Interface to be implemented by objects that define a mapping between
* requests and handler objects.
* @see org.springframework.core.Ordered
* @see org.springframework.web.servlet.handler.AbstractHandlerMapping
* @see org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
* @see org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
*/
public interface HandlerMapping {
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/**
* Return a handler and any interceptors for this request. The choice may be made
* on request URL, session state, or any factor the implementing class chooses.
* <p>The returned HandlerExecutionChain contains a handler Object, rather than
* even a tag interface, so that handlers are not constrained in any way.
* For example, a HandlerAdapter could be written to allow another framework's
* handler objects to be used.
* <p>Returns {@code null} if no match was found. This is not an error.
* The DispatcherServlet will query all registered HandlerMapping beans to find
* a match, and only decide there is an error if none can find a handler.
* @param request current HTTP request
* @return a HandlerExecutionChain instance containing handler object and
* any interceptors, or {@code null} if no mapping found
* @throws Exception if there is an internal error
*/
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
以常用接口实现为例,RequestMappingHandlerMapping
这里提供了Ordered接口,可以确定匹配的顺序,同时通过继承WebApplicationObjectSupport抽象类,可以获取相关Bean(handler),更多细节可查看 类AbstractHandlerMapping。
HandlerAdapter
之所以要使用HandlerAdapter,是因为SpringMVC中并没有对处理器做任何的限制,可以是个类,方法(HandlerMethod)这点从hanlder是Object就可以看出,首先是接口实现
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* MVC framework SPI interface, allowing parameterization of core MVC workflow.
*
* <p>Interface that must be implemented for each handler type to handle a request.
* This interface is used to allow the {@link DispatcherServlet} to be indefinitely
* extensible. The DispatcherServlet accesses all installed handlers through this
* interface, meaning that it does not contain code specific to any handler type.
*
* <p>Note that a handler can be of type {@code Object}. This is to enable
* handlers from other frameworks to be integrated with this framework without
* custom coding, as well as to allow for annotation handler objects that do
* not obey any specific Java interface.
*
* <p>This interface is not intended for application developers. It is available
* to handlers who want to develop their own web workflow.
*
* <p>Note: HandlerAdaptger implementators may implement the
* {@link org.springframework.core.Ordered} interface to be able to specify a
* sorting order (and thus a priority) for getting applied by DispatcherServlet.
* Non-Ordered instances get treated as lowest priority.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
* @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
*/
public interface HandlerAdapter {
/**
* Given a handler instance, return whether or not this HandlerAdapter can
* support it. Typical HandlerAdapters will base the decision on the handler
* type. HandlerAdapters will usually only support one handler type each.
* <p>A typical implementation:
* <p>{@code
* return (handler instanceof MyHandler);
* }
* @param handler handler object to check
* @return whether or not this object can use the given handler
*/
boolean supports(Object handler);
/**
* Use the given handler to handle this request.
* The workflow that is required may vary widely.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler to use. This object must have previously been passed
* to the {@code supports} method of this interface, which must have
* returned {@code true}.
* @throws Exception in case of errors
* @return ModelAndView object with the name of the view and the required
* model data, or {@code null} if the request has been handled directly
*/
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* Same contract as for HttpServlet's {@code getLastModified} method.
* Can simply return -1 if there's no support in the handler class.
* @param request current HTTP request
* @param handler handler to use
* @return the lastModified value for the given handler
* @see javax.servlet.http.HttpServlet#getLastModified
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);
}
其中一个实现类SimpleControllerHandlerAdapter
package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
/**
* Adapter to use the plain {@link Controller} workflow interface with
* the generic {@link org.springframework.web.servlet.DispatcherServlet}.
* Supports handlers that implement the {@link LastModified} interface.
* <p>This is an SPI class, not used directly by application code.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.web.servlet.DispatcherServlet
* @see Controller
* @see LastModified
* @see HttpRequestHandlerAdapter
*/
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
一般默认使用的是 RequestMappingHandlerAdapter ,在其父类中有这样一句设置Order
private int order = Ordered.LOWEST_PRECEDENCE;
HandlerAdapter implementators may implement the Order,使用时候是遍历 handlerAdapters ,调用supports判断直接返回(除了设置的,默认的实现有3个)。注意Order是越低优先级越高的。更多细节可以查看#afterPropertiesSet()方法
总结一下,主要做了三件事,解析参数、执行请求,处理返回结果
- 解析参数的过程中用到的参数来源有多个,大体可分为两类
- 一类是从Model来的(通过FlashMapManager和ModelFactory)
- 另一类是从Request来的, 具体使用HandlerMethodArgumentResolver进行解析(有的是@InitBinder WebDataBinder)
- 执行请求的是用HandlerMethod的子类ServletInvocableHandlerMethod
- 返回值用HandlerMethodReturnValueHandler进行解析。
另外,整个处理过程中ModelAndViewContainer起着参数传递的作用。
这里还有一个需要注意的,就是RequestMappingHandlerAdapter
是怎么注入的,通过debug发现,默认的实现HandlerAdapter 有 3个 ,除了前者还有HttpRequestHandlerAdapte
、SimpleControllerHandlerAdapte
的
通过查询官方文档发现,有两种方式, 会自动生成相关@RequestMapping的类。
- @EnableWebMvc
- 配置文件配置了
mvc:annotation-driven
官方文档截图
HandlerExceptionResolver
根据异常设置ModelAndView,之后交给render方法去渲染,这里要注意它是在render之前工作,所以只作用于解析对请求做处理过程中的产生的异常。
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Interface to be implemented by objects than can resolve exceptions thrown
* during handler mapping or execution, in the typical case to error views.
* Implementors are typically registered as beans in the application context.
*
* <p>Error views are analogous to the error page JSPs, but can be used with
* any kind of exception including any checked exception, with potentially
* fine-granular mappings for specific handlers.
*
* @author Juergen Hoeller
* @since 22.11.2003
*/
public interface HandlerExceptionResolver {
/**
* Try to resolve the given exception that got thrown during on handler execution,
* returning a ModelAndView that represents a specific error page if appropriate.
* <p>The returned ModelAndView may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to,
* or {@code null} for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
其实现SimpleMappingExceptionResolver 可以通过扩展配置,设置自定义类型
可以配合定制自定义Exception,前端统一展示等
ViewResolver
通过viewName来找到 对应的View,View是用来渲染页面的,主要解决用什么模版和用什么规则填入参数
package org.springframework.web.servlet;
import java.util.Locale;
/**
* Interface to be implemented by objects that can resolve views by name.
*
* <p>View state doesn't change during the running of the application,
* so implementations are free to cache views.
*
* <p>Implementations are encouraged to support internationalization,
* i.e. localized view resolution.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.web.servlet.view.InternalResourceViewResolver
* @see org.springframework.web.servlet.view.ResourceBundleViewResolver
* @see org.springframework.web.servlet.view.XmlViewResolver
*/
public interface ViewResolver {
/**
* Resolve the given view by name.
* <p>Note: To allow for ViewResolver chaining, a ViewResolver should
* return {@code null} if a view with the given name is not defined in it.
* However, this is not required: Some ViewResolvers will always attempt
* to build View objects with the given name, unable to return {@code null}
* (rather throwing an exception when View creation failed).
* @param viewName name of the view to resolve
* @param locale Locale in which to resolve the view.
* ViewResolvers that support internationalization should respect this.
* @return the View object, or {@code null} if not found
* (optional, to allow for ViewResolver chaining)
* @throws Exception if the view cannot be resolved
* (typically in case of problems creating an actual View object)
*/
View resolveViewName(String viewName, Locale locale) throws Exception;
}
常用的jsp解析
按照类图可以把继承AbstractCachingViewResolver
(里面两个Map的方式值得学习)分成两类
ResourceBundleViewResolver
可以同时支持多种类型的视图,需要将每一个视图名和对应的视图类型配置到相应的properties文件中,该类的注释中,解释了其用法,
/* <p>This {@code ViewResolver} supports localized view definitions,
* using the default support of {@link java.util.PropertyResourceBundle}.
* For example, the basename "views" will be resolved as class path resources
* "views_de_AT.properties", "views_de.properties", "views.properties" -
* for a given Locale "de_AT"./
UrlBasedViewResolver
系列的解析器都是对单一视图进行解析的,只需找到模版就行,例如
InternalResourceViewResolver
是专门用来解析jsp,FreeMarkerViewResolver
只针对FreeMarker
相关扩展ExecelViewResolver、SimpleMapViewResolver
默认实现是 InternalResourceViewResolver
RequestToViewNameTranslator
比较简单,当handler处理完后,没有View和ViewName的会调用该方法。
/**
* Do we need view name translation?
*/
private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
}
/**
* Translate the supplied request into a default view name.
* @param request current HTTP servlet request
* @return the view name (or {@code null} if no default found)
* @throws Exception if view name translation failed
*/
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return this.viewNameTranslator.getViewName(request);
}
默认实现是RequestToViewNameTranslator
LocaleResolver
用于从request中解析出Locale,默认实现是
package org.springframework.web.servlet.i18n;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.LocaleResolver;
/**
* {@link LocaleResolver} implementation that simply uses the primary locale
* specified in the "accept-language" header of the HTTP request (that is,
* the locale sent by the client browser, normally that of the client's OS).
*
* <p>Note: Does not support {@code setLocale}, since the accept header
* can only be changed through changing the client's locale settings.
*
* @author Juergen Hoeller
* @since 27.02.2003
* @see javax.servlet.http.HttpServletRequest#getLocale()
*/
public class AcceptHeaderLocaleResolver implements LocaleResolver {
public Locale resolveLocale(HttpServletRequest request) {
return request.getLocale();
}
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
在DispatcherServlet#doSerive()方法中, request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
放进request中,这里除了视图解析的时候需要用到,在使用国际化主题的时候也会用到
切换相关,可查看 LocaleChangeInterceptor
ThemeResolver
SpringMVC中和主题相关的类有如下
- ThemeResolver 作用是从request中解析出主题名,默认实现FixedThemeResolver
- ThemeSource 根据主题名找到对应的主题 ,默认使用WebApplicationContext
- Theme 就是主题,包含了具体资源
package org.springframework.web.servlet.theme;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link org.springframework.web.servlet.ThemeResolver} implementation
* that simply uses a fixed theme. The fixed name can be defined via
* the "defaultThemeName" property; out of the box, it is "theme".
*
* <p>Note: Does not support {@code setThemeName}, as the fixed theme
* cannot be changed.
*
* @author Jean-Pierre Pawlak
* @author Juergen Hoeller
* @since 17.06.2003
* @see #setDefaultThemeName
*/
public class FixedThemeResolver extends AbstractThemeResolver {
@Override
public String resolveThemeName(HttpServletRequest request) {
return getDefaultThemeName();
}
@Override
public void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName) {
throw new UnsupportedOperationException("Cannot change theme - use a different theme resolution strategy");
}
}
切换相关,可查看 ThemeChangeInterceptor
MultipartResolver
用于处理文件上传,注意这个组件在DispatcherServlet.properties 中 没有默认实现,首先是接口
package org.springframework.web.multipart;
import javax.servlet.http.HttpServletRequest;
/**
* A strategy interface for multipart file upload resolution in accordance
* with <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.
* Implementations are typically usable both within an application context
* and standalone.
*
* <p>There are two concrete implementations included in Spring, as of Spring 3.1:
* <ul>
* <li>{@link org.springframework.web.multipart.commons.CommonsMultipartResolver} for Jakarta Commons FileUpload
* <li>{@link org.springframework.web.multipart.support.StandardServletMultipartResolver} for Servlet 3.0 Part API
* </ul>
*
* @author Juergen Hoeller
* @author Trevor D. Cook
* @since 29.09.2003
* @see MultipartHttpServletRequest
* @see MultipartFile
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver
* @see org.springframework.web.multipart.support.ByteArrayMultipartFileEditor
* @see org.springframework.web.multipart.support.StringMultipartFileEditor
* @see org.springframework.web.servlet.DispatcherServlet
*/
public interface MultipartResolver {
/**
* Determine if the given request contains multipart content.
* <p>Will typically check for content type "multipart/form-data", but the actually
* accepted requests might depend on the capabilities of the resolver implementation.
* @param request the servlet request to be evaluated
* @return whether the request contains multipart content
*/
boolean isMultipart(HttpServletRequest request);
/**
* Parse the given HTTP request into multipart files and parameters,
* and wrap the request inside a
* {@link org.springframework.web.multipart.MultipartHttpServletRequest} object
* that provides access to file descriptors and makes contained
* parameters accessible via the standard ServletRequest methods.
* @param request the servlet request to wrap (must be of a multipart content type)
* @return the wrapped servlet request
* @throws MultipartException if the servlet request is not multipart, or if
* implementation-specific problems are encountered (such as exceeding file size limits)
* @see MultipartHttpServletRequest#getFile
* @see MultipartHttpServletRequest#getFileNames
* @see MultipartHttpServletRequest#getFileMap
* @see javax.servlet.http.HttpServletRequest#getParameter
* @see javax.servlet.http.HttpServletRequest#getParameterNames
* @see javax.servlet.http.HttpServletRequest#getParameterMap
*/
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
/**
* Cleanup any resources used for the multipart handling,
* like a storage for the uploaded files.
* @param request the request to cleanup resources for
*/
void cleanupMultipart(MultipartHttpServletRequest request);
}
常用实现CommonsMultipartResolver
对于上传类型判断是 multipart/form-data, 两种实现
- 一种是Servlet的标准实现StandardServletMultipartResolver
- 一种是Apache的commons-fileupload方式
FlashMapManager
FlashMap主要用于redirect中传递参数,FlashMapManage是用来管理FlashMap的
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* A strategy interface for retrieving and saving FlashMap instances.
* See {@link FlashMap} for a general overview of flash attributes.
*
* @author Rossen Stoyanchev
* @since 3.1
* @see FlashMap
*/
public interface FlashMapManager {
/**
* Find a FlashMap saved by a previous request that matches to the current
* request, remove it from underlying storage, and also remove other
* expired FlashMap instances.
* <p>This method is invoked in the beginning of every request in contrast
* to {@link #saveOutputFlashMap}, which is invoked only when there are
* flash attributes to be saved - i.e. before a redirect.
* @param request the current request
* @param response the current response
* @return a FlashMap matching the current request or {@code null}
*/
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
/**
* Save the given FlashMap, in some underlying storage and set the start
* of its expiration period.
* <p><strong>NOTE:</strong> Invoke this method prior to a redirect in order
* to allow saving the FlashMap in the HTTP session or in a response
* cookie before the response is committed.
* @param flashMap the FlashMap to save
* @param request the current request
* @param response the current response
*/
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
两个方法,分别是retrieveAndUpdate 用于恢复参数,saveOutputFlashMap用于保存参数
类图也比较简单
默认实现是SessionFlashMapManager
整个redirects 的参数通过FlashMap传递的过程分三步
- 首先,RequestMappingHandlerAapter会将其设置到
outputFlashMap
中,如果是redirect类型的返回类型值(将需要传递的参数设置到outputFlashMap
中,也可以是RedirectAttributes类型的参数中) - 在RedirectView中会调用
#saveOutputFlashMap()
方法,将outputFlashMap
中的参数设置到Session - 请求redirect后,DispatcherServlet会调有你
#retrieveAndUpdate()
方法从Session中获取inputFlashMap
并设置到Request的属性中备用,同时从Session中删除
处理请求
FrameworkServlet
Servlet的处理过程,首先是从Servlet接口的service,然后在HttpServlet的service方法中根据请求的类型不同将请求路由到了doGet、doHead等方法。
FrameworkServlet中重写了doGet等方法。将请求集中到processRequest方法进行统一处理
/**
* Override the parent class implementation in order to intercept PATCH
* requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
processRequest这个方法主要做了两件事情(当然还有doService,和对异步请求对处理)
- 对LocaleContext(获取locale)和RequestAttributes(管理request和session属性)的设置及恢复
- 处理完后发布ServletRequestHandledEvent消息
LocaleContextHolder 、RequestContextHolder
public abstract class LocaleContextHolder {
private static final ThreadLocal localeContextHolder = new NamedThreadLocal("Locale context");
private static final ThreadLocal inheritableLocaleContextHolder = new NamedInheritableThreadLocal("Locale context");
....
}
这里比较有意思的地方是这是个抽象类,里面的方法实现都是静态方法,只能调用不能实例化。RequestContextHolder的实现类似,封装的是ServletRequestAttributes
这里之所以需要对LocaleContext和RequestAttributes恢复,是因为在Servlet外面可能还有别等操作,例如Filter(Spring-MVC的HandlerInterceptor),为了不影响那些操作。
DispatcherServlet
DispatcherServlet是FrameworkServlet的子类,实现了doService方法
doService
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//Pass paramter use to Redirect
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
在doDispatch之前做了
- 判断是否include请求,是的话保存快照(attributesSnapshot)
- 为request设置默认的属性,在后面的handlers和view中需要使用
doDispatch
下面是doDispatch的方法的实现,主要的功能是 Process the actual dispatching to the handler.
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 || mappedHandler.getHandler() == 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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
整个处理过程最核心的代码只有4句
- 根据request请求在HandlerMapping找到对应的Handler
- 根据Handler找到对应的HandlerAdapter
- 用HandlerAdapter处理Handler,返回一个ModelAndView(Controller就是在这个地方执行)
- 根据ModelAndView的信息(或异常处理),通过ViewResolver找到View,并根据Model中的数据渲染输出后给用户
整个#doDispatcher()
处理流程如下
从请求开始的话,当请求到达服务器,服务器(Tomcat connector)分配一个socket线程来连接,创建request和response ,然后交给对应的servlet处理(中间经过Pipeline,Filter),这样请求就从容器(Tomcat)到Servlet(springMVC)
异步请求
http协议是单向的,只能客户端自己拉,不能服务端主动推。
异步请求一般有两种方式,定时轮询或长链接。Servlet对异步的支持是通过长链接的方式。
servlet异步支持
ServletRequest
首先是在 3.0后的 ServletRequest 中可以找到#startAsync()
返回一个异步容器
package javax.servlet;
import java.io.*;
import java.util.*;
/**
* Defines an object to provide client request information to a servlet. The
* servlet container creates a <code>ServletRequest</code> object and passes
* it as an argument to the servlet's <code>service</code> method.
*
* <p>A <code>ServletRequest</code> object provides data including
* parameter name and values, attributes, and an input stream.
* Interfaces that extend <code>ServletRequest</code> can provide
* additional protocol-specific data (for example, HTTP data is
* provided by {@link javax.servlet.http.HttpServletRequest}.
*
* @author Various
*
* @see javax.servlet.http.HttpServletRequest
*
*/
public interface ServletRequest {
/**
* Puts this request into asynchronous mode, and initializes its
* {@link AsyncContext} with the original (unwrapped) ServletRequest
* and ServletResponse objects.
*
* <p>Calling this method will cause committal of the associated
* response to be delayed until {@link AsyncContext#complete} is
* called on the returned {@link AsyncContext}, or the asynchronous
* operation has timed out.
*
* <p>Calling {@link AsyncContext#hasOriginalRequestAndResponse()} on
* the returned AsyncContext will return <code>true</code>. Any filters
* invoked in the <i>outbound</i> direction after this request was put
* into asynchronous mode may use this as an indication that any request
* and/or response wrappers that they added during their <i>inbound</i>
* invocation need not stay around for the duration of the asynchronous
* operation, and therefore any of their associated resources may be
* released.
*
* <p>This method clears the list of {@link AsyncListener} instances
* (if any) that were registered with the AsyncContext returned by the
* previous call to one of the startAsync methods, after calling each
* AsyncListener at its {@link AsyncListener#onStartAsync onStartAsync}
* method.
*
* <p>Subsequent invocations of this method, or its overloaded
* variant, will return the same AsyncContext instance, reinitialized
* as appropriate.
*
* @return the (re)initialized AsyncContext
*
* @throws IllegalStateException if this request is within the scope of
* a filter or servlet that does not support asynchronous operations
* (that is, {@link #isAsyncSupported} returns false),
* or if this method is called again without any asynchronous dispatch
* (resulting from one of the {@link AsyncContext#dispatch} methods),
* is called outside the scope of any such dispatch, or is called again
* within the scope of the same dispatch, or if the response has
* already been closed
*
* @since Servlet 3.0
*/
public AsyncContext startAsync() throws IllegalStateException;
//...
}
异步容器AsyncContext
package javax.servlet;
/**
* Class representing the execution context for an asynchronous operation
* that was initiated on a ServletRequest.
*
* <p>An AsyncContext is created and initialized by a call to
* {@link ServletRequest#startAsync()} or
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
* Repeated invocations of these methods will return the same AsyncContext
* instance, reinitialized as appropriate.
*
* <p>In the event that an asynchronous operation has timed out, the
* container must run through these steps:
* <ol>
* <li>Invoke, at their {@link AsyncListener#onTimeout onTimeout} method, all
* {@link AsyncListener} instances registered with the ServletRequest
* on which the asynchronous operation was initiated.</li>
* <li>If none of the listeners called {@link #complete} or any of the
* {@link #dispatch} methods, perform an error dispatch with a status code
* equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>.</li>
* <li>If no matching error page was found, or the error page did not call
* {@link #complete} or any of the {@link #dispatch} methods, call
* {@link #complete}.</li>
* </ol>
*
* @since Servlet 3.0
*/
public interface AsyncContext {
/**
* The name of the request attribute under which the original
* request URI is made available to the target of a
* {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}
*/
static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
/**
* The name of the request attribute under which the original
* context path is made available to the target of a
* {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}
*/
static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
/**
* The name of the request attribute under which the original
* path info is made available to the target of a
* {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}
*/
static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
/**
* The name of the request attribute under which the original
* servlet path is made available to the target of a
* {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}
*/
static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
/**
* The name of the request attribute under which the original
* query string is made available to the target of a
* {@link #dispatch(String)} or {@link #dispatch(ServletContext,String)}
*/
static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
/**
* Gets the request that was used to initialize this AsyncContext
* by calling {@link ServletRequest#startAsync()} or
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
*
* @return the request that was used to initialize this AsyncContext
*/
public ServletRequest getRequest();
/**
* Gets the response that was used to initialize this AsyncContext
* by calling {@link ServletRequest#startAsync()} or
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}.
*
* @return the response that was used to initialize this AsyncContext
*/
public ServletResponse getResponse();
/**
* Checks if this AsyncContext was initialized with the original or
* application-wrapped request and response objects.
*
* <p>This information may be used by filters invoked in the
* <i>outbound</i> direction, after a request was put into
* asynchronous mode, to determine whether any request and/or response
* wrappers that they added during their <i>inbound</i> invocation need
* to be preserved for the duration of the asynchronous operation, or may
* be released.
*
* @return true if this AsyncContext was initialized with the original
* request and response objects by calling
* {@link ServletRequest#startAsync()}, or if it was initialized by
* calling
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
* and neither the ServletRequest nor ServletResponse arguments
* carried any application-provided wrappers; false otherwise
*/
public boolean hasOriginalRequestAndResponse();
/**
* Dispatches the request and response objects of this AsyncContext
* to the servlet container.
*
* <p>If the asynchronous cycle was started with
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
* and the request passed is an instance of HttpServletRequest,
* then the dispatch is to the URI returned by
* {@link javax.servlet.http.HttpServletRequest#getRequestURI}.
* Otherwise, the dispatch is to the URI of the request when it was
* last dispatched by the container.
*
* <p>The following sequence illustrates how this will work:
* <code><pre>
* // REQUEST dispatch to /url/A
* AsyncContext ac = request.startAsync();
* ...
* ac.dispatch(); // ASYNC dispatch to /url/A
*
* // FORWARD dispatch to /url/B
* getRequestDispatcher("/url/B").forward(request,response);
* // Start async operation from within the target of the FORWARD
* // dispatch
* ac = request.startAsync();
* ...
* ac.dispatch(); // ASYNC dispatch to /url/A
*
* // FORWARD dispatch to /url/B
* getRequestDispatcher("/url/B").forward(request,response);
* // Start async operation from within the target of the FORWARD
* // dispatch
* ac = request.startAsync(request,response);
* ...
* ac.dispatch(); // ASYNC dispatch to /url/B
* </pre></code>
*
* <p>This method returns immediately after passing the request
* and response objects to a container managed thread, on which the
* dispatch operation will be performed.
* If this method is called before the container-initiated dispatch
* that called <tt>startAsync</tt> has returned to the container, the
* dispatch operation will be delayed until after the container-initiated
* dispatch has returned to the container.
*
* <p>The dispatcher type of the request is set to
* <tt>DispatcherType.ASYNC</tt>. Unlike
* {@link RequestDispatcher#forward(ServletRequest, ServletResponse)
* forward dispatches}, the response buffer and
* headers will not be reset, and it is legal to dispatch even if the
* response has already been committed.
*
* <p>Control over the request and response is delegated
* to the dispatch target, and the response will be closed when the
* dispatch target has completed execution, unless
* {@link ServletRequest#startAsync()} or
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)}
* are called.
*
* <p>Any errors or exceptions that may occur during the execution
* of this method must be caught and handled by the container, as
* follows:
* <ol>
* <li>Invoke, at their {@link AsyncListener#onError onError} method, all
* {@link AsyncListener} instances registered with the ServletRequest
* for which this AsyncContext was created, and make the caught
* <tt>Throwable</tt> available via {@link AsyncEvent#getThrowable}.</li>
* <li>If none of the listeners called {@link #complete} or any of the
* {@link #dispatch} methods, perform an error dispatch with a status code
* equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>, and
* make the above <tt>Throwable</tt> available as the value of the
* <tt>RequestDispatcher.ERROR_EXCEPTION</tt> request attribute.</li>
* <li>If no matching error page was found, or the error page did not call
* {@link #complete} or any of the {@link #dispatch} methods, call
* {@link #complete}.</li>
* </ol>
*
* <p>There can be at most one asynchronous dispatch operation per
* asynchronous cycle, which is started by a call to one of the
* {@link ServletRequest#startAsync} methods. Any attempt to perform an
* additional asynchronous dispatch operation within the same
* asynchronous cycle will result in an IllegalStateException.
* If startAsync is subsequently called on the dispatched request,
* then any of the dispatch or {@link #complete} methods may be called.
*
* @throws IllegalStateException if one of the dispatch methods
* has been called and the startAsync method has not been
* called during the resulting dispatch, or if {@link #complete}
* was called
*
* @see ServletRequest#getDispatcherType
*/
public void dispatch();
/**
* Dispatches the request and response objects of this AsyncContext
* to the given <tt>path</tt>.
*
* <p>The <tt>path</tt> parameter is interpreted in the same way
* as in {@link ServletRequest#getRequestDispatcher(String)}, within
* the scope of the {@link ServletContext} from which this
* AsyncContext was initialized.
*
* <p>All path related query methods of the request must reflect the
* dispatch target, while the original request URI, context path,
* path info, servlet path, and query string may be recovered from
* the {@link #ASYNC_REQUEST_URI}, {@link #ASYNC_CONTEXT_PATH},
* {@link #ASYNC_PATH_INFO}, {@link #ASYNC_SERVLET_PATH}, and
* {@link #ASYNC_QUERY_STRING} attributes of the request. These
* attributes will always reflect the original path elements, even under
* repeated dispatches.
*
* <p>There can be at most one asynchronous dispatch operation per
* asynchronous cycle, which is started by a call to one of the
* {@link ServletRequest#startAsync} methods. Any attempt to perform an
* additional asynchronous dispatch operation within the same
* asynchronous cycle will result in an IllegalStateException.
* If startAsync is subsequently called on the dispatched request,
* then any of the dispatch or {@link #complete} methods may be called.
*
* <p>See {@link #dispatch()} for additional details, including error
* handling.
*
* @param path the path of the dispatch target, scoped to the
* ServletContext from which this AsyncContext was initialized
*
* @throws IllegalStateException if one of the dispatch methods
* has been called and the startAsync method has not been
* called during the resulting dispatch, or if {@link #complete}
* was called
*
* @see ServletRequest#getDispatcherType
*/
public void dispatch(String path);
/**
* Dispatches the request and response objects of this AsyncContext
* to the given <tt>path</tt> scoped to the given <tt>context</tt>.
*
* <p>The <tt>path</tt> parameter is interpreted in the same way
* as in {@link ServletRequest#getRequestDispatcher(String)}, except that
* it is scoped to the given <tt>context</tt>.
*
* <p>All path related query methods of the request must reflect the
* dispatch target, while the original request URI, context path,
* path info, servlet path, and query string may be recovered from
* the {@link #ASYNC_REQUEST_URI}, {@link #ASYNC_CONTEXT_PATH},
* {@link #ASYNC_PATH_INFO}, {@link #ASYNC_SERVLET_PATH}, and
* {@link #ASYNC_QUERY_STRING} attributes of the request. These
* attributes will always reflect the original path elements, even under
* repeated dispatches.
*
* <p>There can be at most one asynchronous dispatch operation per
* asynchronous cycle, which is started by a call to one of the
* {@link ServletRequest#startAsync} methods. Any attempt to perform an
* additional asynchronous dispatch operation within the same
* asynchronous cycle will result in an IllegalStateException.
* If startAsync is subsequently called on the dispatched request,
* then any of the dispatch or {@link #complete} methods may be called.
*
* <p>See {@link #dispatch()} for additional details, including error
* handling.
*
* @param context the ServletContext of the dispatch target
* @param path the path of the dispatch target, scoped to the given
* ServletContext
*
* @throws IllegalStateException if one of the dispatch methods
* has been called and the startAsync method has not been
* called during the resulting dispatch, or if {@link #complete}
* was called
*
* @see ServletRequest#getDispatcherType
*/
public void dispatch(ServletContext context, String path);
/**
* Completes the asynchronous operation that was started on the request
* that was used to initialze this AsyncContext, closing the response
* that was used to initialize this AsyncContext.
*
* <p>Any listeners of type {@link AsyncListener} that were registered
* with the ServletRequest for which this AsyncContext was created will
* be invoked at their {@link AsyncListener#onComplete(AsyncEvent)
* onComplete} method.
*
* <p>It is legal to call this method any time after a call to
* {@link ServletRequest#startAsync()} or
* {@link ServletRequest#startAsync(ServletRequest, ServletResponse)},
* and before a call to one of the <tt>dispatch</tt> methods
* of this class.
* If this method is called before the container-initiated dispatch
* that called <tt>startAsync</tt> has returned to the container, then
* the call will not take effect (and any invocations of
* {@link AsyncListener#onComplete(AsyncEvent)} will be delayed) until
* after the container-initiated dispatch has returned to the container.
*/
public void complete();
/**
* Causes the container to dispatch a thread, possibly from a managed
* thread pool, to run the specified <tt>Runnable</tt>. The container may
* propagate appropriate contextual information to the <tt>Runnable</tt>.
*
* @param run the asynchronous handler
*/
public void start(Runnable run);
/**
* Registers the given {@link AsyncListener} with the most recent
* asynchronous cycle that was started by a call to one of the
* {@link ServletRequest#startAsync} methods.
*
* <p>The given AsyncListener will receive an {@link AsyncEvent} when
* the asynchronous cycle completes successfully, times out, or results
* in an error.
*
* <p>AsyncListener instances will be notified in the order in which
* they were added.
*
* @param listener the AsyncListener to be registered
*
* @throws IllegalStateException if this method is called after
* the container-initiated dispatch, during which one of the
* {@link ServletRequest#startAsync} methods was called, has
* returned to the container
*/
public void addListener(AsyncListener listener);
/**
* Registers the given {@link AsyncListener} with the most recent
* asynchronous cycle that was started by a call to one of the
* {@link ServletRequest#startAsync} methods.
*
* <p>The given AsyncListener will receive an {@link AsyncEvent} when
* the asynchronous cycle completes successfully, times out, or results
* in an error.
*
* <p>AsyncListener instances will be notified in the order in which
* they were added.
*
* <p>The given ServletRequest and ServletResponse objects will
* be made available to the given AsyncListener via the
* {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
* {@link AsyncEvent#getSuppliedResponse getSuppliedResponse} methods,
* respectively, of the {@link AsyncEvent} delivered to it. These objects
* should not be read from or written to, respectively, at the time the
* AsyncEvent is delivered, because additional wrapping may have
* occurred since the given AsyncListener was registered, but may be used
* in order to release any resources associated with them.
*
* @param listener the AsyncListener to be registered
* @param servletRequest the ServletRequest that will be included
* in the AsyncEvent
* @param servletResponse the ServletResponse that will be included
* in the AsyncEvent
*
* @throws IllegalStateException if this method is called after
* the container-initiated dispatch, during which one of the
* {@link ServletRequest#startAsync} methods was called, has
* returned to the container
*/
public void addListener(AsyncListener listener,
ServletRequest servletRequest,
ServletResponse servletResponse);
/**
* Instantiates the given {@link AsyncListener} class.
*
* <p>The returned AsyncListener instance may be further customized
* before it is registered with this AsyncContext via a call to one of
* the <code>addListener</code> methods.
*
* <p>The given AsyncListener class must define a zero argument
* constructor, which is used to instantiate it.
*
* <p>This method supports resource injection if the given
* <tt>clazz</tt> represents a Managed Bean.
* See the Java EE platform and JSR 299 specifications for additional
* details about Managed Beans and resource injection.
* <p>This method supports any annotations applicable to AsyncListener.
*
* @param clazz the AsyncListener class to instantiate
*
* @return the new AsyncListener instance
*
* @throws ServletException if the given <tt>clazz</tt> fails to be
* instantiated
*/
public <T extends AsyncListener> T createListener(Class<T> clazz)
throws ServletException;
/**
* Sets the timeout (in milliseconds) for this AsyncContext.
*
* <p>The timeout applies to this AsyncContext once the
* container-initiated dispatch during which one of the
* {@link ServletRequest#startAsync} methods was called has
* returned to the container.
*
* <p>The timeout will expire if neither the {@link #complete} method
* nor any of the dispatch methods are called. A timeout value of
* zero or less indicates no timeout.
*
* <p>If {@link #setTimeout} is not called, then the container's
* default timeout, which is available via a call to
* {@link #getTimeout}, will apply.
*
* @param timeout the timeout in milliseconds
*
* @throws IllegalStateException if this method is called after
* the container-initiated dispatch, during which one of the
* {@link ServletRequest#startAsync} methods was called, has
* returned to the container
*/
public void setTimeout(long timeout);
/**
* Gets the timeout (in milliseconds) for this AsyncContext.
*
* <p>This method returns the container's default timeout for
* asynchronous operations, or the timeout value passed to the most
* recent invocation of {@link #setTimeout}.
*
* <p>A timeout value of zero or less indicates no timeout.
*
* @return the timeout in milliseconds
*/
public long getTimeout();
}
比较简单,通过看代码中的注释就能知其大意了,异步就是不等待结果立即返回,这里start一般用新线程的方式,线程运行完后(#complete),需要通知(#addListener),以及超时检测处理(#setTimeout,由容器Tomcat来设置)
AsyncListener监听器
package javax.servlet;
import java.io.IOException;
import java.util.EventListener;
/**
* Listener that will be notified in the event that an asynchronous
* operation initiated on a ServletRequest to which the listener had been
* added has completed, timed out, or resulted in an error.
*
* @since Servlet 3.0
*/
public interface AsyncListener extends EventListener {
/**
* Notifies this AsyncListener that an asynchronous operation
* has been completed.
*
* <p>The {@link AsyncContext} corresponding to the asynchronous
* operation that has been completed may be obtained by calling
* {@link AsyncEvent#getAsyncContext getAsyncContext} on the given
* <tt>event</tt>.
*
* <p>In addition, if this AsyncListener had been registered via a call
* to {@link AsyncContext#addListener(AsyncListener,
* ServletRequest, ServletResponse)}, the supplied ServletRequest and
* ServletResponse objects may be retrieved by calling
* {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
* {@link AsyncEvent#getSuppliedResponse getSuppliedResponse},
* respectively, on the given <tt>event</tt>.
*
* @param event the AsyncEvent indicating that an asynchronous
* operation has been completed
*
* @throws IOException if an I/O related error has occurred during the
* processing of the given AsyncEvent
*/
public void onComplete(AsyncEvent event) throws IOException;
/**
* Notifies this AsyncListener that an asynchronous operation
* has timed out.
*
* <p>The {@link AsyncContext} corresponding to the asynchronous
* operation that has timed out may be obtained by calling
* {@link AsyncEvent#getAsyncContext getAsyncContext} on the given
* <tt>event</tt>.
*
* <p>In addition, if this AsyncListener had been registered via a call
* to {@link AsyncContext#addListener(AsyncListener,
* ServletRequest, ServletResponse)}, the supplied ServletRequest and
* ServletResponse objects may be retrieved by calling
* {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
* {@link AsyncEvent#getSuppliedResponse getSuppliedResponse},
* respectively, on the given <tt>event</tt>.
*
* @param event the AsyncEvent indicating that an asynchronous
* operation has timed out
*
* @throws IOException if an I/O related error has occurred during the
* processing of the given AsyncEvent
*/
public void onTimeout(AsyncEvent event) throws IOException;
/**
* Notifies this AsyncListener that an asynchronous operation
* has failed to complete.
*
* <p>The {@link AsyncContext} corresponding to the asynchronous
* operation that failed to complete may be obtained by calling
* {@link AsyncEvent#getAsyncContext getAsyncContext} on the given
* <tt>event</tt>.
*
* <p>In addition, if this AsyncListener had been registered via a call
* to {@link AsyncContext#addListener(AsyncListener,
* ServletRequest, ServletResponse)}, the supplied ServletRequest and
* ServletResponse objects may be retrieved by calling
* {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
* {@link AsyncEvent#getSuppliedResponse getSuppliedResponse},
* respectively, on the given <tt>event</tt>.
*
* @param event the AsyncEvent indicating that an asynchronous
* operation has failed to complete
*
* @throws IOException if an I/O related error has occurred during the
* processing of the given AsyncEvent
*/
public void onError(AsyncEvent event) throws IOException;
/**
* Notifies this AsyncListener that a new asynchronous cycle is being
* initiated via a call to one of the {@link ServletRequest#startAsync}
* methods.
*
* <p>The {@link AsyncContext} corresponding to the asynchronous
* operation that is being reinitialized may be obtained by calling
* {@link AsyncEvent#getAsyncContext getAsyncContext} on the given
* <tt>event</tt>.
*
* <p>In addition, if this AsyncListener had been registered via a call
* to {@link AsyncContext#addListener(AsyncListener,
* ServletRequest, ServletResponse)}, the supplied ServletRequest and
* ServletResponse objects may be retrieved by calling
* {@link AsyncEvent#getSuppliedRequest getSuppliedRequest} and
* {@link AsyncEvent#getSuppliedResponse getSuppliedResponse},
* respectively, on the given <tt>event</tt>.
*
* <p>This AsyncListener will not receive any events related to the
* new asynchronous cycle unless it registers itself (via a call
* to {@link AsyncContext#addListener}) with the AsyncContext that
* is delivered as part of the given AsyncEvent.
*
* @param event the AsyncEvent indicating that a new asynchronous
* cycle is being initiated
*
* @throws IOException if an I/O related error has occurred during the
* processing of the given AsyncEvent
*/
public void onStartAsync(AsyncEvent event) throws IOException;
}
监听器主要有4个事件,开始、完成、超时、错误。
SpringMVC中的相关组件
SpringMVC中异步请求相关组件 AsyncWebRequest、WebAsyncManager、WebAsyncUtils
AsyncWebRequest
其实现有两个,其中NoSupportAsyncWebRequest
不支持异步,所以我们只需要关注StandarServletAsyncWebRequest
即可。(可以看到其实主要是对Servelt中支持异步的类的一些特性进行整合)
具体细节看StandarServletAsyncWebRequest的实现
WebAsyncManager
package org.springframework.web.context.request.async;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.async.DeferredResult.DeferredResultHandler;
import org.springframework.web.util.UrlPathHelper;
/**
* The central class for managing asynchronous request processing, mainly intended
* as an SPI and not typically used directly by application classes.
*
* <p>An async scenario starts with request processing as usual in a thread (T1).
* Concurrent request handling can be initiated by calling
* {@link #startCallableProcessing(Callable, Object...) startCallableProcessing} or
* {@link #startDeferredResultProcessing(DeferredResult, Object...) startDeferredResultProcessing},
* both of which produce a result in a separate thread (T2). The result is saved
* and the request dispatched to the container, to resume processing with the saved
* result in a third thread (T3). Within the dispatched thread (T3), the saved
* result can be accessed via {@link #getConcurrentResult()} or its presence
* detected via {@link #hasConcurrentResult()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*
* @see org.springframework.web.context.request.AsyncWebRequestInterceptor
* @see org.springframework.web.servlet.AsyncHandlerInterceptor
* @see org.springframework.web.filter.OncePerRequestFilter#shouldNotFilterAsyncDispatch
* @see org.springframework.web.filter.OncePerRequestFilter#isAsyncDispatch
*/
public final class WebAsyncManager {
private static final Object RESULT_NONE = new Object();
private static final Log logger = LogFactory.getLog(WebAsyncManager.class);
private static final UrlPathHelper urlPathHelper = new UrlPathHelper();
private static final CallableProcessingInterceptor timeoutCallableInterceptor =
new TimeoutCallableProcessingInterceptor();
private static final DeferredResultProcessingInterceptor timeoutDeferredResultInterceptor =
new TimeoutDeferredResultProcessingInterceptor();
private AsyncWebRequest asyncWebRequest;
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(this.getClass().getSimpleName());
private Object concurrentResult = RESULT_NONE;
private Object[] concurrentResultContext;
private final Map<Object, CallableProcessingInterceptor> callableInterceptors =
new LinkedHashMap<Object, CallableProcessingInterceptor>();
private final Map<Object, DeferredResultProcessingInterceptor> deferredResultInterceptors =
new LinkedHashMap<Object, DeferredResultProcessingInterceptor>();
/**
* Package private constructor.
* @see WebAsyncUtils#getAsyncManager(javax.servlet.ServletRequest)
* @see WebAsyncUtils#getAsyncManager(org.springframework.web.context.request.WebRequest)
*/
WebAsyncManager() {
}
/**
* Configure the {@link AsyncWebRequest} to use. This property may be set
* more than once during a single request to accurately reflect the current
* state of the request (e.g. following a forward, request/response
* wrapping, etc). However, it should not be set while concurrent handling
* is in progress, i.e. while {@link #isConcurrentHandlingStarted()} is
* {@code true}.
*
* @param asyncWebRequest the web request to use
*/
public void setAsyncWebRequest(final AsyncWebRequest asyncWebRequest) {
Assert.notNull(asyncWebRequest, "AsyncWebRequest must not be null");
Assert.state(!isConcurrentHandlingStarted(), "Can't set AsyncWebRequest with concurrent handling in progress");
this.asyncWebRequest = asyncWebRequest;
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
asyncWebRequest.removeAttribute(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
}
});
}
/**
* Configure an AsyncTaskExecutor for use with concurrent processing via
* {@link #startCallableProcessing(Callable, Object...)}.
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used.
*/
public void setTaskExecutor(AsyncTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* Whether the selected handler for the current request chose to handle the
* request asynchronously. A return value of "true" indicates concurrent
* handling is under way and the response will remain open. A return value
* of "false" means concurrent handling was either not started or possibly
* that it has completed and the request was dispatched for further
* processing of the concurrent result.
*/
public boolean isConcurrentHandlingStarted() {
return ((this.asyncWebRequest != null) && this.asyncWebRequest.isAsyncStarted());
}
/**
* Whether a result value exists as a result of concurrent handling.
*/
public boolean hasConcurrentResult() {
return (this.concurrentResult != RESULT_NONE);
}
/**
* Provides access to the result from concurrent handling.
*
* @return an Object, possibly an {@code Exception} or {@code Throwable} if
* concurrent handling raised one.
* @see #clearConcurrentResult()
*/
public Object getConcurrentResult() {
return this.concurrentResult;
}
/**
* Provides access to additional processing context saved at the start of
* concurrent handling.
*
* @see #clearConcurrentResult()
*/
public Object[] getConcurrentResultContext() {
return this.concurrentResultContext;
}
/**
* Get the {@link CallableProcessingInterceptor} registered under the given key.
* @param key the key
* @return the interceptor registered under that key or {@code null}
*/
public CallableProcessingInterceptor getCallableInterceptor(Object key) {
return this.callableInterceptors.get(key);
}
/**
* Get the {@link DeferredResultProcessingInterceptor} registered under the given key.
* @param key the key
* @return the interceptor registered under that key or {@code null}
*/
public DeferredResultProcessingInterceptor getDeferredResultInterceptor(Object key) {
return this.deferredResultInterceptors.get(key);
}
/**
* Register a {@link CallableProcessingInterceptor} under the given key.
* @param key the key
* @param interceptor the interceptor to register
*/
public void registerCallableInterceptor(Object key, CallableProcessingInterceptor interceptor) {
Assert.notNull(key, "Key is required");
Assert.notNull(interceptor, "CallableProcessingInterceptor is required");
this.callableInterceptors.put(key, interceptor);
}
/**
* Register a {@link CallableProcessingInterceptor} without a key.
* The key is derived from the class name and hashcode.
* @param interceptors one or more interceptors to register
*/
public void registerCallableInterceptors(CallableProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "A CallableProcessingInterceptor is required");
for (CallableProcessingInterceptor interceptor : interceptors) {
String key = interceptor.getClass().getName() + ":" + interceptor.hashCode();
this.callableInterceptors.put(key, interceptor);
}
}
/**
* Register a {@link DeferredResultProcessingInterceptor} under the given key.
* @param key the key
* @param interceptor the interceptor to register
*/
public void registerDeferredResultInterceptor(Object key, DeferredResultProcessingInterceptor interceptor) {
Assert.notNull(key, "Key is required");
Assert.notNull(interceptor, "DeferredResultProcessingInterceptor is required");
this.deferredResultInterceptors.put(key, interceptor);
}
/**
* Register a {@link DeferredResultProcessingInterceptor} without a key.
* The key is derived from the class name and hashcode.
* @param interceptors one or more interceptors to register
*/
public void registerDeferredResultInterceptors(DeferredResultProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "A DeferredResultProcessingInterceptor is required");
for (DeferredResultProcessingInterceptor interceptor : interceptors) {
String key = interceptors.getClass().getName() + ":" + interceptors.hashCode();
this.deferredResultInterceptors.put(key, interceptor);
}
}
/**
* Clear {@linkplain #getConcurrentResult() concurrentResult} and
* {@linkplain #getConcurrentResultContext() concurrentResultContext}.
*/
public void clearConcurrentResult() {
this.concurrentResult = RESULT_NONE;
this.concurrentResultContext = null;
}
/**
* Start concurrent request processing and execute the given task with an
* {@link #setTaskExecutor(AsyncTaskExecutor) AsyncTaskExecutor}. The result
* from the task execution is saved and the request dispatched in order to
* resume processing of that result. If the task raises an Exception then
* the saved result will be the raised Exception.
*
* @param callable a unit of work to be executed asynchronously
* @param processingContext additional context to save that can be accessed
* via {@link #getConcurrentResultContext()}
* @throws Exception If concurrent processing failed to start
*
* @see #getConcurrentResult()
* @see #getConcurrentResultContext()
*/
@SuppressWarnings({"unchecked", "rawtypes" })
public void startCallableProcessing(final Callable<?> callable, Object... processingContext) throws Exception {
Assert.notNull(callable, "Callable must not be null");
startCallableProcessing(new WebAsyncTask(callable), processingContext);
}
/**
* Use the given {@link WebAsyncTask} to configure the task executor as well as
* the timeout value of the {@code AsyncWebRequest} before delegating to
* {@link #startCallableProcessing(Callable, Object...)}.
*
* @param webAsyncTask a WebAsyncTask containing the target {@code Callable}
* @param processingContext additional context to save that can be accessed
* via {@link #getConcurrentResultContext()}
* @throws Exception If concurrent processing failed to start
*/
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) throws Exception {
Assert.notNull(webAsyncTask, "WebAsyncTask must not be null");
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
Long timeout = webAsyncTask.getTimeout();
if (timeout != null) {
this.asyncWebRequest.setTimeout(timeout);
}
AsyncTaskExecutor executor = webAsyncTask.getExecutor();
if (executor != null) {
this.taskExecutor = executor;
}
List<CallableProcessingInterceptor> interceptors = new ArrayList<CallableProcessingInterceptor>();
interceptors.add(webAsyncTask.getInterceptor());
interceptors.addAll(this.callableInterceptors.values());
interceptors.add(timeoutCallableInterceptor);
final Callable<?> callable = webAsyncTask.getCallable();
final CallableInterceptorChain interceptorChain = new CallableInterceptorChain(interceptors);
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
@Override
public void run() {
logger.debug("Processing timeout");
Object result = interceptorChain.triggerAfterTimeout(asyncWebRequest, callable);
if (result != CallableProcessingInterceptor.RESULT_NONE) {
setConcurrentResultAndDispatch(result);
}
}
});
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
interceptorChain.triggerAfterCompletion(asyncWebRequest, callable);
}
});
interceptorChain.applyBeforeConcurrentHandling(asyncWebRequest, callable);
startAsyncProcessing(processingContext);
this.taskExecutor.submit(new Runnable() {
@Override
public void run() {
Object result = null;
try {
interceptorChain.applyPreProcess(asyncWebRequest, callable);
result = callable.call();
}
catch (Throwable t) {
result = t;
}
finally {
result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result);
}
setConcurrentResultAndDispatch(result);
}
});
}
private void setConcurrentResultAndDispatch(Object result) {
synchronized (WebAsyncManager.this) {
if (hasConcurrentResult()) {
return;
}
concurrentResult = result;
}
if (asyncWebRequest.isAsyncComplete()) {
logger.error("Could not complete async processing due to timeout or network error");
return;
}
logger.debug("Concurrent result value [" + concurrentResult + "]");
logger.debug("Dispatching request to resume processing");
asyncWebRequest.dispatch();
}
/**
* Start concurrent request processing and initialize the given
* {@link DeferredResult} with a {@link DeferredResultHandler} that saves
* the result and dispatches the request to resume processing of that
* result. The {@code AsyncWebRequest} is also updated with a completion
* handler that expires the {@code DeferredResult} and a timeout handler
* assuming the {@code DeferredResult} has a default timeout result.
*
* @param deferredResult the DeferredResult instance to initialize
* @param processingContext additional context to save that can be accessed
* via {@link #getConcurrentResultContext()}
* @throws Exception If concurrent processing failed to start
*
* @see #getConcurrentResult()
* @see #getConcurrentResultContext()
*/
public void startDeferredResultProcessing(
final DeferredResult<?> deferredResult, Object... processingContext) throws Exception {
Assert.notNull(deferredResult, "DeferredResult must not be null");
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
Long timeout = deferredResult.getTimeoutValue();
if (timeout != null) {
this.asyncWebRequest.setTimeout(timeout);
}
List<DeferredResultProcessingInterceptor> interceptors = new ArrayList<DeferredResultProcessingInterceptor>();
interceptors.add(deferredResult.getInterceptor());
interceptors.addAll(this.deferredResultInterceptors.values());
interceptors.add(timeoutDeferredResultInterceptor);
final DeferredResultInterceptorChain interceptorChain = new DeferredResultInterceptorChain(interceptors);
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
@Override
public void run() {
try {
interceptorChain.triggerAfterTimeout(asyncWebRequest, deferredResult);
}
catch (Throwable t) {
setConcurrentResultAndDispatch(t);
}
}
});
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
interceptorChain.triggerAfterCompletion(asyncWebRequest, deferredResult);
}
});
interceptorChain.applyBeforeConcurrentHandling(asyncWebRequest, deferredResult);
startAsyncProcessing(processingContext);
try {
interceptorChain.applyPreProcess(this.asyncWebRequest, deferredResult);
deferredResult.setResultHandler(new DeferredResultHandler() {
@Override
public void handleResult(Object result) {
result = interceptorChain.applyPostProcess(asyncWebRequest, deferredResult, result);
setConcurrentResultAndDispatch(result);
}
});
}
catch (Throwable t) {
setConcurrentResultAndDispatch(t);
}
}
private void startAsyncProcessing(Object[] processingContext) {
clearConcurrentResult();
this.concurrentResultContext = processingContext;
this.asyncWebRequest.startAsync();
if (logger.isDebugEnabled()) {
HttpServletRequest request = this.asyncWebRequest.getNativeRequest(HttpServletRequest.class);
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Concurrent handling starting for " + request.getMethod() + " [" + requestUri + "]");
}
}
}
类中有两个重要的方法#startCallableProcessing(用于处理Callable和WebAsyncTask类型),#startDeferredResultProcessing(用于处理DeferredResult和ListenableFuture类型),是启动异步处理的入口方法,它们一共做了三件事
- 启动异步处理
- 给Request设置相应属性(timeout、timeoutHandler和completionHandler)
- 在相应的位置调用相应的拦截器(CallableProcessingInterceptor和DeferredResultProcessingInterceptor都封装在相应的Chain中)
更多细节 可查看startCallableProcessing的执行过程
WebAsyncUtils
package org.springframework.web.context.request.async;
import java.lang.reflect.Constructor;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
/**
* Utility methods related to processing asynchronous web requests.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class WebAsyncUtils {
public static final String WEB_ASYNC_MANAGER_ATTRIBUTE = WebAsyncManager.class.getName() + ".WEB_ASYNC_MANAGER";
private static Constructor<?> standardAsyncRequestConstructor;
/**
* Obtain the {@link WebAsyncManager} for the current request, or if not
* found, create and associate it with the request.
*/
public static WebAsyncManager getAsyncManager(ServletRequest servletRequest) {
WebAsyncManager asyncManager = (WebAsyncManager) servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE);
if (asyncManager == null) {
asyncManager = new WebAsyncManager();
servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager);
}
return asyncManager;
}
/**
* Obtain the {@link WebAsyncManager} for the current request, or if not
* found, create and associate it with the request.
*/
public static WebAsyncManager getAsyncManager(WebRequest webRequest) {
int scope = RequestAttributes.SCOPE_REQUEST;
WebAsyncManager asyncManager = (WebAsyncManager) webRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, scope);
if (asyncManager == null) {
asyncManager = new WebAsyncManager();
webRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager, scope);
}
return asyncManager;
}
/**
* Create an AsyncWebRequest instance. By default an instance of
* {@link StandardServletAsyncWebRequest} is created if running in Servlet
* 3.0 (or higher) environment or as a fallback, an instance of
* {@link NoSupportAsyncWebRequest} is returned.
*
* @param request the current request
* @param response the current response
* @return an AsyncWebRequest instance, never {@code null}
*/
public static AsyncWebRequest createAsyncWebRequest(HttpServletRequest request, HttpServletResponse response) {
return ClassUtils.hasMethod(ServletRequest.class, "startAsync") ?
createStandardServletAsyncWebRequest(request, response) : new NoSupportAsyncWebRequest(request, response);
}
private static AsyncWebRequest createStandardServletAsyncWebRequest(HttpServletRequest request, HttpServletResponse response) {
try {
if (standardAsyncRequestConstructor == null) {
String className = "org.springframework.web.context.request.async.StandardServletAsyncWebRequest";
Class<?> clazz = ClassUtils.forName(className, WebAsyncUtils.class.getClassLoader());
standardAsyncRequestConstructor = clazz.getConstructor(HttpServletRequest.class, HttpServletResponse.class);
}
return (AsyncWebRequest) BeanUtils.instantiateClass(standardAsyncRequestConstructor, request, response);
}
catch (Throwable t) {
throw new IllegalStateException("Failed to instantiate StandardServletAsyncWebRequest", t);
}
}
}
可以看到这个类主要是提供WebAsyncManage、AsyncWebRequest 相关的操作
SpringMVC中请求的支持
FrameworkServlet
中添加了RequestBindingInterceptor
RequestMappingHandlerAdapter
的#invokeHandlerMethod
提供了对异步请求的核心支持 2.1 创建
AsyncWebRequest
并设置超时时间 2.2 对当前请求
WebAsyncManager
设置了属性(taskExecutor、asyncWebRequest、callabltinterceptors和deferredResultInterceptors)
2.3 并判断是否有结果,如果有对结果进行处理。
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
2.4 然后执行方法
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
返回值处理器,
AsyncTaskMethodReturnValueHandler
CallableMethodReturnValueHandler
DeferredResultMethodReturnValueHandler
ListenableFutureReturnValueHandler
DispatcherServlet
的#doDispatcher
,如果是异步直接返回。
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
*/
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
}
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
spring webFlux
等待填坑
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com