SpringMVC源码解析之Servlet
SpringMVC源码解析之GenericServlet和HttpServlet
SpringMVC源码解析之DispatcherServlet:生命周期init()和destroy()
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
//其它的doPost, doDelete, doPut方法也类似
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
与HttpServlet将请求按照请求方式的不同分发给doXXX方法处理不同,FrameworkServlet除了增加了PATCH请求的处理以外,还将所有的请求(TRACE和OPTIONS请求不一定)处理又集中到了processRequest方法。
至于为什么不直接覆盖service方法,而需要在doXXX一一覆盖?是因为将请求分发给doXXX是HttpServlet中的一种规范,如果是直接在service方法中覆盖sevice方法并在其中直接调用proceeRequest固然可以实现,但是这样子doXXX方法将毫无意义,如果在子类中需要针对某个特定类型的请求(如POST)做处理,无法直接通过覆盖doPost实现,而是需要覆盖service方法,不符合正常的逻辑,而且一般情况下开发者不需要对SpringMVC的内部结构和实现有足够了解。
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//dispatchOptionsRequest为true(默认false) || cores的预请求
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
processRequest(request, response);
if (response.containsHeader("Allow")) {
// Proper OPTIONS response coming from a handler - we're done.
return;
}
}
//交给父类处理,会在父类的处理结果上加上PATCH
// Use response wrapper in order to always add PATCH to the allowed methods
super.doOptions(request, new HttpServletResponseWrapper(response) {
@Override
public void setHeader(String name, String value) {
if ("Allow".equals(name)) {
value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
}
super.setHeader(name, value);
}
});
}
dispatchOptionsRequest为true(默认false)或者 cores的预请求 会进入processRequest方法,否则按照HttpServlet#doOptions方法处理,但是处理结果会加上PATCH。因为FrameworkServlet类增加了对OPTIONS请求的处理。
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//dispatchTraceRequest为true(默认false)
if (this.dispatchTraceRequest) {
processRequest(request, response);
if ("message/http".equals(response.getContentType())) {
// Proper TRACE response coming from a handler - we're done.
return;
}
}
super.doTrace(request, response);
}
dispatchTraceRequest为true时会进入 processRequest方法。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取原来的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的LocalContext
LocaleContext localeContext = buildLocaleContext(request);
//获取原来的RequestAttributes,RequestContextHolder是ThreadLocal类型
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的RequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//异步请求的处理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将当前请求的LocaleContext和RequestContext设置到ThreadLocal中
initContextHolders(request, localeContext, requestAttributes);
try {
//核心逻辑,模板方法
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//ThreadLocal中的LocaleContext和RequestAttributes恢复成原来的对象
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) { //标志请求完成
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//发布ServletRequestHanldedEvrnt事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
该方法主要做了3件事
(1)LocaleContext和RequestAttributes的管理
(2)异步请求的处理,注入WebAsyncManager
(3)处理完成后,requestAttributes.requestCompleted()标记请求完成,发布ServletRequestHanldedEvrnt事件
//设置LocaleContext和RequestContext
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
//恢复LocalContext和RequestAttributes
private void resetContextHolders(HttpServletRequest request,
@Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) {
LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
}
LocaleContextHolder和RequestContextHolder通过LocalThread类型对属性值进行管理。
以LocaleContextHolder为例,这是一个abstract类。
private static final ThreadLocal
new NamedThreadLocal<>(“LocaleContext”);
private static final ThreadLocal inheritableLocaleContextHolder =
new NamedInheritableThreadLocal<>(“LocaleContext”);
内部有两个static final的常量,两者都是ThreadLocal的子类型,其中inheritableLocaleContextHolder可以被子线程继承。
默认由localeContextHolder维护属性LocaleContext,如果属性是可继承的,则由inheritableLocaleContextHolder进行维护。
RequestContextHolder的实现方式和LocaleContextHolder基本一致。
LocaleContext对Locale实例进行管理,储存的是本地化信息(如zh-cn)
RequestAttributes可以对Request和Session的attribute进行管理(get/set/remove),根据scope判断操作的是Request还是Session。
public void requestCompleted() {
executeRequestDestructionCallbacks();
updateAccessedSessionAttributes();
this.requestActive = false;
}
(1)执行所有请求完成的回调,请求完成的回调线程类保存在requestDestructionCallbacks中,完成后进行清空
private void executeRequestDestructionCallbacks() {
synchronized (this.requestDestructionCallbacks) {
for (Runnable runnable : this.requestDestructionCallbacks.values()) {
runnable.run();
}
this.requestDestructionCallbacks.clear();
}
}
(2)更新session属性
protected void updateAccessedSessionAttributes() {
if (!this.sessionAttributesToUpdate.isEmpty()) {
// Update all affected session attributes.
HttpSession session = getSession(false);
if (session != null) {
try {
for (Map.Entry entry : this.sessionAttributesToUpdate.entrySet()) {
String name = entry.getKey();
Object newValue = entry.getValue();
Object oldValue = session.getAttribute(name);
if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) {
session.setAttribute(name, newValue);
}
}
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
this.sessionAttributesToUpdate.clear();
}
}
(3)修改标志变量requestActive
//发布请求已经处理的事件ServletRequestHanldedEvrnt
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
前面的文章已经提到,在DispatcherServlet#onRefresh()方法中初始化9个策略组件,这些组件会在请求处理时发挥作用,下面先简单提一下每个组件的作用。
(1)MultipartResolver
multipart解析器,处理文件上传。
如果在配置了MultipartResolver,每个请求都会被检查是否包含multipart。如果包含,会交给MultipartResolver进行解析。
(2)LocaleResolver
本地化信息的解析器,指定本地化信息Locale。
(3)ThemeResolver
主题资源解析器,指定主题Theme。Web开发中一般通过主题控制网页风格,一个主题就是一组静态,如换肤功能。
(4)HandlerMapping & HandlerAdapter
处理器映射
SpringMVC将请求的具体处理逻辑称为处理器handler,处理器可以是一个类,也可以是一个方法,也可以是其它方式,因此处理器handler的类型为Object.
为了保证同意调用,SpringMVC提供了一个适配接口HandlerAdapter,对handler进行封装。
HandlerMapping称为处理器映射,根据请求找到对应的HandlerExecutionChain(包括具体的处理器Handler和拦截器集Interceptors)
(5)HandlerExceptionResolver
处理异常解析器,在请求处理过程中出现异常时进行处理。
(6)ViewResolver
视图解析器,根据逻辑视图(视图名)到物理视图
(7)RequestToViewNameTranslator
在Handler没有设置视图时,从请求中获取逻辑视图
(8)FlashMapManager
对FlashMap进行管理,FlashMap可以在redirect转发时传递参数
/**
* 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 {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
//对于include请求,保存一份请求属性的快照
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
//设置请求属性,后续handler和view中需要进行使用
// 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());
//用于请求redirect转发,FlashMap用于转发时传递参数,否则只能在url中携带参数
if (this.flashMapManager != null) {
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()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
//还原include请求的快照属性
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
(1)属性设置,用于后续handler和view以及redirect转发的处理
(2)include请求在处理前对属性做快照,处理后进行恢复
(3)具体的请求处理方法交给doDispatcher处理
对于FlashMapManager,用于管理请求转发时携带属性。
正常情况下,direct是没有携带参数的功能的,如果需要携带参数,只能加在url,但是url有长度限制而且是显式的。
SpringMVC提供了FlashMap用来在direct时携带参数,具体的方式如下:
((FlashMap((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)).put(“key”, “value”);
SpringMVC本身也提供了对应功能的封装:
RedirectAttributes#addFlashAttribute:参数保存在FlashMap中。
RedirectAttributes#addAttribute:参数拼接到url中。
/**
* Process the actual dispatching to the handler.
* 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.
*
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 {
//检查并处理multipart请求,如果是multipart请求会处理成MultipartHttpServletRequest返回
processedRequest = checkMultipart(request);
//是否是multipart请求
multipartRequestParsed = (processedRequest != request);
//根据HandlerMapping找到对应的HandlerExecutionChain
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//没有找到handler时的处理
noHandlerFound(processedRequest, response);
return;
}
//获取对应的HandlerAdapter
// 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);
//GET || HEAD请求 的Last-Modified缓存
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;
}
}
//执行Interceptor集的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//执行HanlderAdapter的请求处理逻辑
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//异步处理请求
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//没有设置view时提供默认view进行设置
applyDefaultViewName(processedRequest, mv);
//执行Interceptor集的postHandle
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);
}
//针对processDispatchResult方法捕捉其中抛出的Exception和Error
catch (Exception ex) {
//执行Interceptor集的afterCompletion
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 {
//multipart请求清理,将上传的文件删除
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//异常处理
if (exception != null) {
//ModelAndViewDefiningException类型,会携带对应的ModelAndView
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//由对应的处理器handler进行异常处理,返回ModelAndView。其中使用了HandlerExceptionResolver。
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方法负责页面的渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//触发Interceptor的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
对请求的执行结果和异常进行处理
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
//根据LocaleResolver获取Locale
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
//根据ViewResolver由viewName获取View
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
protected View resolveViewName(String viewName, @Nullable Map model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
本地化信息Locale解析,视图View解析,页面渲染,主题Theme解析