目录
一、刨析前准备
1、前端控制器 DispatcherServlet 继承结构
2、重要时机点分析
1)Handler 方法的执行时机
打断点
观察调用栈
doDispathch 方法中的 1064 行代码完成 handler 方法的调用。
2)⻚面渲染时机(打断点并观察调用栈)
3、Spring MVC 处理请求的流程
Spring MVC 处理请求的流程即为 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法的执行过程,其中步骤 2、3、4、5 是核心步骤(下面 4 中源码步骤)。
1)调用 getHandler() 获取到能够处理当前请求的执行链 HandlerExecutionChain(Handler + 拦截器)。
但是如何去 getHandler 的 ? 后面进行分析。
2)调用 getHandlerAdapter() 获取能够执行 (1) 中 Handler 的适配器。
但是如何去 getHandlerAdapter 的 ? 后面进行分析。
3)适配器调用 Handler 执行 ha.handle(总会返回一个 ModelAndView 对象)。
4)调用 processDispatchResult() 方法完成视图渲染跳转。
4、DispatcherServlet 类中的方法 doDispatch 源码
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 {
// 1 检查是否是文件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
/*
2 取得处理当前请求的 Controller,这里也称为 Handler,即处理器。
这里并不是直接返回 Controller,而是返回 HandlerExecutionChain
请求处理链对象。
该对象封装了 Handler 和 Inteceptor。
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果 handler 为空,则返回 404。
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3 获取处理请求的处理器适配器 HandlerAdapter。
HandlerAdapter ha = getHandlerAdapter(mappedHandler
.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request,
mappedHandler.getHandler());
if (new ServletWebRequest(request, response)
.checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest,
response)) {
return;
}
// Actually invoke the handler.
// 4 实际处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response,
mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
// As of 4.3, we're processing Errors
// thrown from handler methods as well,
// making them available for @ExceptionHandler
// methods and other scenarios.
dispatchException = new NestedServletException(
"Handler dispatch failed", err);
}
// 5 跳转⻚面,渲染视图
processDispatchResult(processedRequest, response, mappedHandler,
mv, dispatchException);
} catch (Exception ex) {
// 最终会调用 HandlerInterceptor 的 afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
ex);
} catch (Throwable err) {
// 最终会调用 HandlerInterceptor 的 afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed",
err));
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(
processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
二、源码跟踪刨析
1、核心步骤 getHandler 方法剖析
遍历两个 HandlerMapping,试图获取能够处理当前请求的执行链。
2、核心步骤 getHandlerAdapter 方法剖析
遍历各个 HandlerAdapter,看哪个 Adapter 支持处理当前 Handler。
3、核心步骤 ha.handle 方法剖析
1)入口
2)根据断点,跟踪源码
RequestMappingHandlerAdapter#handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)
throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 判断当前是否需要支持在同一个 session 中只能线性地处理请求
if (this.synchronizeOnSession) { // synchronizeOnSession : false
// 获取当前请求的 session 对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前 session 生成一个唯一的可以用于锁定的 key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对 HandlerMethod 进行参数等的适配处理,并调用目标 handler
mav = invokeHandlerMethod(request, response,
handlerMethod);
}
} else {
// No HttpSession available -> no mutex necessary
// 如果当前不存在 session,则直接对 HandlerMethod 进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// No synchronization on session demanded at all...
// 如果当前不需要对 session 进行同步处理,则直接对 HandlerMethod 进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod)
.hasSessionAttributes()) {
applyCacheSeconds(response,
this.cacheSecondsForSessionAttributeHandlers);
} else {
prepareResponse(response);
}
}
return mav;
}
RequestMappingHandlerAdapter#invokeHandlerMethod
// 其他代码 略
// 对请求参数进行处理,调用目标 HandlerMethod,
// 并且将返回值封装为一个 ModelAndView 对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
ServletInvocableHandlerMethod#invokeAndHandle
// 对目标 handler 的参数进行处理,并且调用目标 handler
Object returnValue = invokeForRequest(webRequest, mavContainer,
providedArgs);
// 其他代码 略
InvocableHandlerMethod#invokeForRequest
@Nullable
public Object invokeForRequest(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 将 request 中的参数转换为当前 handler 的参数形式
Object[] args = getMethodArgumentValues(request, mavContainer,
providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 这里 doInvoke() 方法主要是结合处理后的参数,使用反射对目标方法进行调用
return doInvoke(args);
}
4、核心步骤 processDispatchResult 方法剖析
1)render 方法完成渲染。
DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request,
HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler,
@Nullable ModelAndView mv, @Nullable Exception exception)
throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered",
exception);
mv = ((ModelAndViewDefiningException) exception)
.getModelAndView();
} else {
Object handler = (mappedHandler != null ? mappedHandler
.getHandler() : null);
mv = processHandlerException(request, response, handler,
exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 这里
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request)
.isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
2)视图解析器解析出 View 视图对象。
DispatcherServlet#resolveViewName
3)在解析出 View 视图对象的过程中会判断是否重定向、是否转发等,不同的情况封装的是不同的 View 实现。
UrlBasedViewResolver#createView
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
InternalResourceView view = new InternalResourceView(forwardUrl);
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
}
// Else fall back to superclass implementation: calling loadView.
// 跟踪到这里
return super.createView(viewName, locale);
}
4)解析出 View 视图对象的过程中,要将逻辑视图名解析为物理视图名。
UrlBasedViewResolver#buildView
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class");
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils
.instantiateClass(viewClass);
// 逻辑视图名转换为物理视图名,解析出物理视图名(拼接前缀和后缀)。
view.setUrl(getPrefix() + viewName + getSuffix());
view.setAttributesMap(getAttributesMap());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
String requestContextAttribute = getRequestContextAttribute();
if (requestContextAttribute != null) {
view.setRequestContextAttribute(requestContextAttribute);
}
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
5)封装 View 视图对象之后,调用了 view 对象的 render 方法。
DispatcherServlet#render 方法中。
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 + "]", ex);
}
throw ex;
}
6)渲染数据。
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() + ", model " +
(model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " +
this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request,
response);
prepareResponse(request, response);
// 跟踪到这。
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
7)把 modelMap 中的数据暴露到 request 域中,这也是为什么后台 model.add 之后在 jsp 中可以从请求域取出来的根本原因。
InternalResourceView#renderMergedOutputModel
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
// 跟踪到这里。
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for ["
+ getUrl() + "]: Check that the corresponding file exists"
+ " within your web application archive!");
}
// If already included or response already committed, perform include,
// else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
} else {
// Note: The forwarded resource is supposed to determine
// the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
8)将数据设置到请求域中。
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
} else {
request.removeAttribute(name);
}
});
}
三、Spring MVC 九大组件初始化
1、在 DispatcherServlet 中定义了九个属性,每一个属性都对应一种组件。
/** MultipartResolver used by this servlet. */
// 多部件解析器
@Nullable
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
// 主题解析器
@Nullable
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
// 处理器映射器组件
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
// 处理器适配器组件
@Nullable
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
// flash 属性管理组件
@Nullable
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;
九大组件都是定义了接口,接口其实就是定义了该组件的规范,比如 ViewResolver、HandlerAdapter 等都是接口。
2、九大组件的初始化时机
1)DispatcherServlet 中的 onRefresh(),该方法中初始化了九大组件。
@Override
protected void onRefresh(ApplicationContext context) {
// 初始化策略
initStrategies(context);
}
// 初始化九大组件
protected void initStrategies(ApplicationContext context) {
// 多文件上传到组件
initMultipartResolver(context);
// 初始化本地语言环境
initLocaleResolver(context);
// 初始化模板处理器
initThemeResolver(context);
// 初始化 HandlerMapping
initHandlerMappings(context);
// 初始化参数适配器
initHandlerAdapters(context);
// 初始化异常拦截器
initHandlerExceptionResolvers(context);
// 初始化视图预处理器
initRequestToViewNameTranslator(context);
// 初始化 FlashMap 管理器
initFlashMapManager(context);
// 初始化视图转换器
initViewResolvers(context);
}
2)观察其中的一个组件的初始化 initHandlerMappings(context)。
DispatcherServlet#initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext,
// including ancestor contexts.
// 按照 HandlerMapping.class 类型去 ioc 容器中找到所有的 HandlerMapping。
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
// 否则在 ioc 中按照固定名称 id(handlerMapping)去找
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME,
HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 最后还为空则按照默认策略生成
// 按照默认方式实例化生成 HandlerMapping
this.handlerMappings = getDefaultStrategies(context,
HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '"
+ getServletName() + "': using default strategies"
+ " from DispatcherServlet.properties");
}
}
}
3)如果按照类型和按照固定 id 从 ioc 容器中找不到对应组件,则会按照默认策略进行注册初始化,默认策略在 DispatcherServlet.properties 文件中配置。
DispatcherServlet#getDefaultStrategies
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context,
Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils
.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className,
DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
} catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default "
+ "strategy class [" + className
+ "] for interface [" + key + "]", ex);
} catch (LinkageError err) {
throw new BeanInitializationException("Unresolvable "
+ "class definition for DispatcherServlet's "
+ "default strategy class [" + className
+ "] for interface [" + key + "]", err);
}
}
return strategies;
} else {
return new LinkedList<>();
}
}
DispatcherServlet.properties
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.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
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
4)注意:多部件解析器的初始化必须按照 id 注册对象(multipartResolver)。
DispatcherServlet#initMultipartResolver
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(
MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass()
.getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '"
+ MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
文章内容输出来源:拉勾教育Java高薪训练营;