springMVC流程简单分析
本文是基于springboot对MVC的流程的一些解析,如有错误,还望指正。
下图是找的一张SpringMvc的执行流程图,后文都是按图中的顺序进行讲解。
话不多说,开始
- 1、用户请求
- 2、通过调度器将请求发送给处理器映射器
- 3、在处理器映射器中找到对应的处理器
- 4、将处理器与已注册的拦截器,封装为HandlerExecutionChain,并返回给调度器进行具体打操作
- 5----8、找到能够使用的处理器适配器来处理该handler
- 9、然后就是视图解析,解析成功后返回给用户
1、请求
当我们输入请求后,程序就会调用doService方法,
//该方法来自:DispatcherServlet.class
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
* 翻译:将DispatcherServlet特定的请求属性和委托公开给{@link#doDispatch}进行实际调度。
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//与日志相关,咱就不讨论了
logRequest(request);
// 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;
//检查确定给定的请求是否是包含请求,即不是来自外部的顶级HTTP请求。
//检查是否存在“javax.servlet.include.request\u uri”请求属性。
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
//获取所有的属性名
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
/**
* this.cleanupAfterInclude :是否在包含请求后执行请求属性的清理?
* attrName.startsWith(DEFAULT_STRATEGIES_PREFIX):检查获取的属性名的前缀是不是: * org.springframework.web.servlet
* attributesSnapshot.put(attrName, request.getAttribute(attrName)):
* 保留请求属性的快照
*/
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
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());
/**
* 做过Web开发的人都知道,后端有请求转发和请求重定向两种方式,
* 请求转发的时候Request是同一个,所以可以在转发后拿到转发前的所有信息;
* 但是重定向后 Request是新的,如果需要在重定向前设置一些信息,重定向后获取使用应该怎么办法呢?
* 这就是 FlashMap存在的意义,FlashMap 借助 session 重定向前通过 FlashMapManager将信息放入FlashMap,
* 重定向后 再借助 FlashMapManager 从 session中找到重定向后需要的 FalshMap
* 如果想要深入的了解,可以搜搜:FlashMap,FlashMapManager
*/
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);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
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);
}
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
2、调度器(DispatchServlet)
//该方法来自:DispatcherServlet.class
/**
* 处理对处理程序的实际分派。
* 处理程序将通过按顺序应用servlet的HandlerMappings获得。
* HandlerAdapter将通过查询servlet安装的HandlerAdapter来获得,
* 以找到第一个支持handler类的HandlerAdapter。
*所有HTTP方法都由此方法处理。由HandlerAdapter或处理程序自己决定哪些方法是可接受的。
*
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//处理程序执行链
/**
* 当用户请求到到DispaterServlet中后,
* 配置的HandlerMapping会根据用户请求(也就是handler)
* 会将它(也就是handler)与所有的interceptors封装为HandlerExecutionChain对象
*
*/
HandlerExecutionChain mappedHandler = null;
//文件上传解析
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否为复合请求(文件上传),如果是,将其解析,并封装好后返回。
processedRequest = checkMultipart(request);
//这个判断的意义在于:如果这是个复合请求,那么它和原请求就不一样,就会为false,反之则不是。
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//1、确定当前请求的处理程序。下面会重点谈谈
mappedHandler = getHandler(processedRequest);
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.
//处理上次修改的标头(如果处理程序支持)。例如:GET、POST...
String method = request.getMethod();
//判断是否为GET请求
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
//获取最后修改时间
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//调用已注册拦截器的预处理方法。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用处理程序。
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,也可以是要解析为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);
}
}
}
}
3、处理器获取
doDispatch()方法将获取的请求,在处理器映射集中找到最优解,然后将获取的handler与一些拦截器封装之后在返回,封装后的对象
1、getHandler()
//该方法来自:DispatcherServlet.class
//返回此请求的HandlerExecutionChain。按顺序尝试所有处理程序映射。
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;
}
//AbstractHandlerMapping类
/**
* AbstractHandlerMapping类是接口HandlerMapping(处理器映射)的抽象实现
* 查找给定请求的处理程序,如果找不到特定的处理程序,则返回默认处理程序
*/
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//查找给定请求的处理程序,如果找不到特定的处理程序,则返回null。此方法由getHandler调用;如果设置了空的返回值,则返回默认处理程序。
//通过调用该方法,咋就获得了对应的处理器。重点内容在下面细说:2、处理器
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
//确保存在拦截器和其他拦截器的缓存lookupPath
if (!ServletRequestPathUtils.hasCachedPath(request)) {
//初始化用于请求映射的路径。
initLookupPath(request);
}
/**
* getHandlerExecutionChain(handler, request):此方法会将用户请求(也就是handler)
* 会将它(也就是handler)与所有的interceptors封装为HandlerExecutionChain对象
* interceptors包含:
* 1、conversionService:数据转换的拦截器,因为一般情况下MVC中的V在用户的客户端,而C则在服务端,
* 当客户端向服务端提交数据时,不能再以java中的对象形式进行传输了,这时候就需要序列化和反序列化来帮助实现了
* 2、resourceUrlProvider:用于获取客户端访问静态资源时应使用的公共URL路径的中心组件。
* 此类知道用于服务静态资源的Spring MVC处理程序映射,
* 并使用已配置ResourceHttpRequestHandler的ResourceResolver链来做出决策。
* 3、....
*/
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//判断该处理是不是夸资源共享
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
2、getHandlerInternal()
//来自RequestMappingInfoHandlerMapping.java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
//来自AbstractHandlerMethodMapping.java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//初始化用于请求映射的路径。
String lookupPath = initLookupPath(request);
//使用getMappings和getMappingsByUrl时获取读锁。保证线程安全
this.mappingRegistry.acquireReadLock();
try {
//(重点)获取处理方法
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//handlerMethod.createWithResolvedBean():如果提供的实例包含bean名称而不是对象实例,则在创建并返回HandlerMethod之前解析bean名称。
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//结束后释放锁
this.mappingRegistry.releaseReadLock();
}
}
3、lookupHandlerMethod()
//来自AbstractHandlerMethodMapping.java
//查找当前请求的最佳匹配处理程序方法。如果找到多个匹配项,则选择最佳匹配项。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//返回给定URL路径的匹配项。不是线程安全的。
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
//通过获取的路径,找到对应的处理方法,并将其添加到匹配映射集中
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
//如果你获取了两个匹配的处理器,就会报错,下面就是关于这个的逻辑实现。
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
//向request添加相关的属性,把关于处理器方法以及映射地址通通添加到request中,方便获取
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
else {
//这是没有找到相关处理器对应的手段
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
4、适配器获取
我直接解析调用handler不就行了为啥还要使用HandlerAdapter包装一层呢?
这就不得不说说handler的类型:
- 以实现了Controller接口的Handler类
- 以@RequestMapping注解修饰的HandlerMethod对象
- 其他还有实现Servlet的实例,HandlerFunction实例、HttpRequestHandler实例等
不同的实例对象调用时走不同的方法,为了能将不同的方法转换成统一的调用形式,这里使用了适配器模式,将各个实例的方法调用包装
到HandlerAdapter统一调用
public interface HandlerAdapter {
/**
* 该方法判断该适配器类是否支持解析调用Handler对象,因为Adapter被设计成了一个只适配执行某一种类型比如Controller接口类型或
*
* @RequestMapping类型的Handler需要使用SimpleControllerHandlerAdapter来进行处理,该方法在执行handler()方法之前需要使用该
*
* 方法来判断是否支持调用这种类型的Handler对象。
*/
boolean supports(Object handler);
//解析并调用Handler对象的方法执行业务逻辑,从request请求中获取参数,执行handler并将响应结果放入reponse对象
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* 获取请求的资源的最终修改时间,如果请求的资源未被修改则直接使用浏览器缓存,这个方法主要是针对http请求为
* get/head请求为提高性能的缓存处理。
*/
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
当获取返回的处理器后,将其交于对应的处理器适配器处理。
//来自DispatcherServlet.java
//返回此处理程序对象的HandlerAdapter。
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");
}
5、视图解析
然后会执行ha.handle(),返回一个ModelAndView的对象。
具体实现:
//来自AbstractHandlerMethodAdapter.java
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//来自RequestMappingHandlerAdapter.java
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//如果有的话,检查给定的请求以获得支持的方法和所需的会话
checkRequest(request);
//如果需要,在同步块中执行invokeHandlerMethod。
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
在上述源码中,最重要的一行就是
mav = invokeHandlerMethod(request, response, handlerMethod);
在该方法中,会调用invokeAndHandle():此方法的就是来处理请求的
//来自ServletInvocableHandlerMethod.java
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//通过请求,获取对应的处理器处理,然后将处理获取的值返回。
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//将返回的结果处理渲染后,返回到服务器中
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
//HandlerMethodReturnValueHandlerComposite.java
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//获取对应的处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//进行处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
//RequestResponseBodyMethodProcessor.java
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
通过调用writeWithMessageConverters(),程序会获取我们请求链接的响应值,以及该值的类型…
然后将响应值经过渲染后返回给用户。