1:入口
我们知道根据servlet规范,servlet调用的入口方法是service
,在springmvc中的这个servlet就是org.springframework.web.servlet.DispatcherServlet
,但是我们看其源码会发现,并没有这个入口的service方法
,实际上该方法是在其父类org.springframework.web.servlet.FrameworkServlet
中,源码如下:
org.springframework.web.servlet.FrameworkServlet#service
@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);
}
}
因为本文分析的是GET
请求的处理流程,因此会调用super.service(request, response)
,到这里也就找到了我们的入口程序
了,好的,那么我们现在就正式开始吧!
2:FrameworkServlet#service
源码在org.springframework.web.servlet.FrameworkServlet
,如下:
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <1>
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// <2>
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else { // <3>
super.service(request, response);
}
}
<1>
处代码是通过org.springframework.http.HttpMethod
获取请求方法对象,是一个枚举,通过map进行维护,用到了枚举提供的values
方法获取所有枚举,源码如下:
public enum HttpMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
static {
for (HttpMethod httpMethod : values()) {
mappings.put(httpMethod.name(), httpMethod);
}
}
@Nullable
public static HttpMethod resolve(@Nullable String method) {
return (method != null ? mappings.get(method) : null);
}
public boolean matches(String method) {
return (this == resolve(method));
}
}
<2>
处代码是单独处理PATCH
请求。<3>
处是我们要分析的重点,会调用父类,即javax.servlet.HttpServlet
的service
方法,开始这个过程。
3:HttpServlet#service
通过org.springframework.web.servlet.FrameworkServlet
调用super.service(req, res)
会执行到javax.servlet.http.HttpServlet#service(req, res)
方法,源码如下:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
代码中除了正常的doXXX
方法调用外,主要是一些缓存相关的逻辑判断,不影响我们的流程,可忽略,因为我们的是GET
请求,则会调用doGet(req, res)
方法,而该方法在org.springframework.web.servlet.FrameworkServlet
中重写了,因此会调用到这里。
4:FrameworkServlet#doGet
源码如下:
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
直接委托给了processReqeust(req, res)
方法,这里doGet其实只是起到了中转调用
的作用。
5:FrameworkServlet#processRequest
该方法中其实也并没有什么核心逻辑,源码如下:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 记录开始处理请求的开始时间,这样能够通过日志监控处理时长
long startTime = System.currentTimeMillis();
// 记录异常信息
Throwable failureCause = null;
try {
// <1>
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
// <1>
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
<1>
处代码为重点,该方法是一个抽象的模板方法,具体实现在类org.springframework.web.servlet.DispatcherServlet
中。<2>
是在请求处理完毕后发布requesthandled
事件,从而记录相关请求处理相关信息,如,处理时间,处理错误信息等。
6:FrameworkServlet#doService
直接看源码:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
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));
}
}
}
// <1>
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());
// <2>
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 {
// <3>
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
<1>
处代码是将spring相关的一些对象设置到request中,如Servlet WebApplicationContext,本地化组件等。<2>
处代码是通过重定向组件来处理重定向数据,其中代码request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
中将Map数据放到一个不可修改的map中,因为只是为了传递数据,这样做可以有效的防止由于误操作导致一些bug的发生,是一种很好的编程方式,值得学习。<3>
处代码是是真正处理请求的地方。
7:DispatcherServlet#doDispatch
可以配合这里来进行学习,看源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// <1>
HttpServletRequest processedRequest = request;
// <2>
HandlerExecutionChain mappedHandler = null;
// <3>
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// <4>
ModelAndView mv = null;
// <5>
Exception dispatchException = null;
try {
// <6>
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// <7>
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// <8>
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// <9>
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// <10>
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// <11>
applyDefaultViewName(processedRequest, mv);
// <12>
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
// <13>
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);
}
// <14>
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// <15>
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
<1>,<2>,<3>,<4>,<5>
代码都是记录相关变量,<6>
处检查是否为文件上传请求,如果是则将HttpServletRequest转换为专门处理文件上的类MultipartHttpServletRequest,<7>
通过请求获取对应的业务handler,获取方法也比较简单,直接循环所有的HandlerMapping,找到封装有对应请求handler的handlermapping,从而获取handler,源码如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
返回的对象是org.springframework.web.servlet.HandlerExecutionChain
对象,封装了目标handler+HandlerInterceptor
,简单看下HandlerInterceptor源码:
public interface HandlerInterceptor {
// 在调用目标handler之前调用该方法
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 在调用目标handler之后调用该方法
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 在目标handler执行完毕(渲染结束)之后调用该方法(一般通过事件触发方式调用)
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
一共3个方法preHandle
,postHandle
,afterCompletion
,执行时机已经在注释中说明,可以看下。<8>
处代码是获取支持handler的HandlerAdapter(HandlerAdapter的作用是屏蔽不同handler调用方式的差异性)
,获取方法和获取Handler一样也是通过循环的方式,源码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
其中方法support(handler)
用来判断当前handler适配器是否支持目标handler,如果是支持则是我们需要的HandlerAdapter,直接返回即可。<9>
为执行HandlerInterceptor的preHandle方法,注意该方法要返回true,否则后续逻辑都不会执行了,使用的过程中一定要小心。<10>
处代码为调用handler(一般就是我们在controller中标注了@RequestMapping的方法),返回就是ModelAndView,<11>
处代码为在没有视图的情况下,应用默认的视图名称。<12>
处代码为调用HandlerInterceptor的postHandle方法。<13>
为如果是发生了异常则记录异常对象到全局变量。<14>
为处理请求结果,正常的,异常的都会处理。<15>
处代码判断如果是文件上传请求,则处理产生的临时文件。接下来看下processDispatchResult
是如何处理最终的视图等信息的。
8:DispatcherServlet#processDispatchResult
源码:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// <1>
boolean errorView = false;
// <2>
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// <3>
if (mv != null && !mv.wasCleared()) {
// 具体渲染页面的方法
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// <4>
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
<1>
处为是否是异常视图的标记,如果是获取了异常视图则该值会被修改为true,<2>
处代码为如果是发生了异常,则获取异常视图,一般为通过processHandlerException
方法使用HandleExceptionViewResolver
组件生成ModelAndView,方法源码如下:
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
做法也比较简单,就是通过循环HandlerExceptionResolver
组件数组handlerExceptionResolvers
找到错误视图ModelAndView。另外注意代码WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
是设置相关的错误信息到request中,<3>处代码为渲染页面,后续详细分析,渲染完毕后如果是错误页面则调用
WebUtils.clearErrorRequestAttributes(request);清理相关错误信息。
<4>处代码为页面渲染完毕后触发HandlerInterceptor的afterCompletion方法。接下来看页面渲染方法render。
9:DispatcherServlet#render
源码:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// <1>
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
//<2>
if (viewName != null) {
// 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 {
// <3>
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.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
// <4>
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// <5>
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
<1>
处代码获取本地话信息,并设置到响应中,<2>
处为在有viewName情况下解析出View,<3>
处为没有viewName情况下直接通过ModelAndView获取View,<4>
处设置响应状态码,<5>
使用ViewResolver
组件完成页面最终的渲染。