How does SpringMVC distribute parsed views from requests
First understand the 9 major components of SpringMVC
// 文件解析
/** 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. */
@Nullable
private FlashMapManager flashMapManager;
// 视图解析器
/** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;
1. Receive htttp request
2. The filtering request contains a series of filtering are generally dofilter method
3. Use DispatchSerlet.class to process the request (core class)
4. Use handleMappings to find the Controller and target method that should be processed according to the request
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
How to find it?
@Nullable
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;
}
It is simple to traverse all HandlerMapping
In the process of initializing HandlerMapping, there is at least one handlerMapping
// handlerMappings的初始化
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
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 {
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) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
5. Select the appropriate handlerAdapter according to the handler
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
6. Execute the target method and generate ModelAndView
adapter
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Test for POJO, the String type actually only has a return value, so at this time, mav = null;
7. Return results according to ModelAndView
View render view, skip loading view if not
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
The flow chart is as follows
DispatcherServlet core method 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 {
// 是否为文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到处理这个请求的Controller
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// 没有找到处理的Controller准备返回
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 处理适配器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "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;
}
// 生成ModelAndView (最最最核心的部分)
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 渲染ModelAndView 到页面
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 {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
result
Obviously SpringMVC usesAdapter mode
Choose different classes according to different methods