SpringMVC 請求處理流程
SpringMVC運(yùn)行流程圖.jpg
1. DispatcherServlet 九大組件
//DispatcherServlet中的九大組件夕膀、全是接口萨赁,我們完全可以自定義實(shí)現(xiàn)。Spring默認(rèn)也都準(zhǔn)備好了這些組件的實(shí)現(xiàn)
/** MultipartResolver used by this servlet. */ //https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet-special-bean-types
@Nullable //文件上傳解析器
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
@Nullable //國際化解析器 Locale(區(qū)域信息)
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
@Nullable //主題解析器
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
@Nullable //Handler(處理器、能處理請求的人(Controller))的映射:【保存的就是所有請求都由誰來處理的映射關(guān)系】
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
@Nullable //Handler(處理器岛心、能處理請求的人(Controller))的適配器题画;【超級反射工具】
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable //Handler的異常解析器,異常處理功能
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
@Nullable //把請求轉(zhuǎn)成視圖名(我們要跳轉(zhuǎn)的頁面地址)的翻譯器【沒啥用】
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
@Nullable //閃存管理器
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
@Nullable //視圖解析器(我們?nèi)ツ男╉撁嫜勾ⅲ趺催^去鲜漩?)
private List<ViewResolver> viewResolvers;
2. DispatcherServlet 初始化
/**
* This implementation calls {@link #initStrategies}.
*/
@Override //
protected void onRefresh(ApplicationContext context) {
initStrategies(context); //初始化九大組件
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) { //初始化所有策略,九大組件在這里進(jìn)行了初始化
initMultipartResolver(context); //容器中有就用集惋,沒有就是null 解析請求
initLocaleResolver(context); //從容器中獲取孕似,沒有用默認(rèn) 國際化
initThemeResolver(context); //從容器中獲取,沒有用默認(rèn) 主題
initHandlerMappings(context); //從容器中獲取刮刑,沒有用默認(rèn) (處理器喉祭、能處理請求的人(Controller))的映射:【保存的就是所有請求都由誰來處理的映射關(guān)系】
initHandlerAdapters(context); //從容器中獲取,沒有用默認(rèn) Handler(處理器雷绢、能處理請求的人(Controller))的適配器臂拓;【超級反射工具】
initHandlerExceptionResolvers(context); //從容器中獲取,沒有用默認(rèn) Handler的異常解析器习寸,異常處理功能
initRequestToViewNameTranslator(context); //Spring沒有濃重說他胶惰,//從容器中獲取,沒有用默認(rèn) 初始化視圖轉(zhuǎn)發(fā)
initViewResolvers(context); //從容器中獲取霞溪,沒有用默認(rèn) 初始化視圖解析器孵滞,將ModelAndView保存的視圖信息,轉(zhuǎn)換為一個視圖鸯匹,輸出數(shù)據(jù)
initFlashMapManager(context); //從容器中獲取坊饶,沒有用默認(rèn) 閃存管理器
}
我們可以在這里打斷點(diǎn),發(fā)現(xiàn)進(jìn)入這里的方法是FrameworkServlet->initWebApplicationContext->configureAndRefreshWebApplicationContext(cwac); //配置并且刷新容器
通過監(jiān)聽事件回調(diào)到onRefresh殴蓬。
2.1 加載默認(rèn)策略
private void initLocaleResolver(ApplicationContext context) {
try { //容器中先來獲取
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default. 容器中沒有匿级,讀取默認(rèn)配置文件進(jìn)行加載
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); //獲取默認(rèn)策略
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
這邊獲取默認(rèn)配置文件策略可以看下
3. DispatcherServlet doService
@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; //把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)); //快照所有屬性
}
}
}
//基本的東西保存到request域中方便處理 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());
if (this.flashMapManager != null) { //閃存管理器(重定向攜帶數(shù)據(jù))
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); //【核心】處理派發(fā)功能
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
3.1【核心】處理派發(fā)功能
//SpringMVC處理請求的核心流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null; //handler(目標(biāo)方法)的執(zhí)行鏈
boolean multipartRequestParsed = false; //文件上傳標(biāo)志
//對異步請求的支持(Servlet3.0以后才有的,Webflux)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request); //檢查當(dāng)前是否文件上傳請求
multipartRequestParsed = (processedRequest != request);
//構(gòu)造出了【目標(biāo)方法+攔截器整個鏈路】決定使用哪個Handler處理當(dāng)前請求 Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { //如果找不到人處理染厅,就send 404
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;
}
}
//所有攔截器的 preHandle 執(zhí)行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; //使用 mappedHandler整個鏈
}
//真正來執(zhí)行目標(biāo)方法 Actually invoke the handler.(反射執(zhí)行目標(biāo)方法、確定參數(shù)值肖粮,處理返回值【封裝成ModelAndView】)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv); //所有攔截器的postHandle
}
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);
} //處理結(jié)果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) { //下面的即使執(zhí)行完孤页,異常還是拋出去
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.1.1 mappedHandler = getHandler(processedRequest); 構(gòu)造出了【目標(biāo)方法+攔截器整個鏈路】決定使用哪個Handler處理當(dāng)前請求
@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;
}
HandlerMapping 在我們注冊的九大組件的時候初始化進(jìn)來的允坚。
進(jìn)去看看這個方法 HandlerExecutionChain handler = mapping.getHandler(request);
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request); //HandlerMapping的registry中找映射,返回HandlerMethod蛾号,真正執(zhí)行當(dāng)前請求的方法
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
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
//找到前面的目標(biāo)方法以后稠项,還要構(gòu)造一個處理器鏈;
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
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;
}
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //尋找當(dāng)前請求誰能處理
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
就是在mappingRegistry 里面查找誰能夠處理當(dāng)前的請求。那么mappingRegistry什么時候注冊進(jìn)去的鲜结?
注冊mappingRegistry
//分析所有的Controller皿渗;里面的每一個@RequestMapping 注解才能知道這個事情
public void registerMapping(T mapping, Object handler, Method method) { //哪個請求由哪個方法處理會通過這個進(jìn)行注冊
if (logger.isTraceEnabled()) {
logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
}
this.mappingRegistry.register(mapping, handler, method);
}
AbstractHandlerMethodMapping
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
我們可以在這里打個斷點(diǎn)看看
我們發(fā)現(xiàn)AbstractHandlerMethodMapping 是繼承InitializingBean。我們之前挖坑的轻腺。后置增強(qiáng)會調(diào)用afterPropertiesSet方法,這個可以自己去跟一下
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果有異常處理異常划乖,以下if內(nèi)全是異常處理環(huán)節(jié)
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else { //定義無數(shù)種異常解析器就會得到不同的異常解析效果
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception); //處理異常贬养,所有的異常解析器都不能干活,這個異常就拋出去了
errorView = (mv != null);
}
}
//上面所有的異常解析器都沒能處理這個異常琴庵,下面直接炸....
// 動態(tài)策略误算。 Did the handler return a view to render? 為啥?@ResponseBody(提前在解析返回值的時候迷殿,就已經(jīng)把數(shù)據(jù)寫出去了儿礼,所以這一步就沒有了)
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); //渲染ModeAndView,來解析模型和視圖庆寺;最終決定響應(yīng)效果
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);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
//默認(rèn)AcceptHeaderLocaleResolver會根據(jù)請求頭中的Accept-Language字段決定瀏覽器能接受哪種中文/英文 頁面
View view; //視圖
String viewName = mv.getViewName(); //適配器執(zhí)行完目標(biāo)方法以后返回的ModelAndView對象里面有 index.jsp
if (viewName != null) {
//************ 把目標(biāo)方法的返回值字符串index.jsp 真正轉(zhuǎn)為能用的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.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
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;
}
}