04-postHandle

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 实际处理时所用的request,如果不是上传请求,则直接使用接收到的request,否则封装成上传类型的request
        HttpServletRequest processedRequest = request;
        // 处理请求的处理器链(包含处理器和对应的interceptor)
        HandlerExecutionChain mappedHandler = null;
        // 是不是上传请求的标志
        boolean multipartRequestParsed = false;

        // 获取异步管理器
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            // 封装model和view的容器
            ModelAndView mv = null;
            // 处理请求过程中抛出的异常,但是不包含渲染过程中抛出的异常
            Exception dispatchException = null;

            try {
                // 1. 上传请求,则通过multipartResolver将其封装成MultipartHttpServletRequest
                processedRequest = checkMultipart(request);
                // 设置上传请求标志
                multipartRequestParsed = (processedRequest != request);

                // 2. Determine handler for the current request.
                // 获取请求对应的HandlerExecutionChain对象(HandlerMethod、HandlerInterceptor)
                mappedHandler = getHandler(processedRequest);
                // 获取不到,则根据配置抛出异常、返回404错误
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 3. Determine handler adapter for the current request.
                // 获得当前handler对应的HandlerAdapter对象
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 4. Process last-modified header, if supported by the handler.
                // 处理GET、HEAD请求的Last-Modified
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    // 获取请求中Service端最后被修改时间
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                // 5. 执行响应的Interceptor的preHandler
                // 注意:该方法如果有一个拦截器的前置处理返回false,则开始倒序触发所有的拦截器的 已完成处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 6. Actually invoke the handler.
                // 真正的调用handler方法,也就是执行对应的方法,并返回视图
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 7. 需要异步处理,直接返回
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                // 8. 当view为空时,根据request设置默认的view
                applyDefaultViewName(processedRequest, mv);

                // 9. 执行响应的interceptor的postHandler
                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);
            }
            // 10. 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion
            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));
        }
        // 11. 释放资源
        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. applyDefaultViewName()

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * 视图名称转换器
     * RequestToViewNameTranslator used by this servlet.
     */
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;

    /**
     * Do we need view name translation?
     */
    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
        // 1. 没有返回自定义视图
        if (mv != null && !mv.hasView()) {
            String defaultViewName = getDefaultViewName(request);
            if (defaultViewName != null) {
                mv.setViewName(defaultViewName);
            }
        }
    }

    /**
     * Translate the supplied request into a default view name.
     *
     * @param request current HTTP servlet request
     * @return the view name (or {@code null} if no default found)
     * @throws Exception if view name translation failed
     */
    @Nullable
    protected String getDefaultViewName(HttpServletRequest request) throws Exception {
        // 1... DefaultRequestToViewNameTranslator
        return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
    }

}















 
 
















 



org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

1. DftReqToViewNameTranslator

public class DefaultRequestToViewNameTranslator implements RequestToViewNameTranslator {

    private static final String SLASH = "/";

	/**
	 * 前缀
	 */
	private String prefix = "";

	/**
	 * 后缀
	 */
	private String suffix = "";

	/**
	 * Translates the request URI of the incoming {@link HttpServletRequest}
	 * into the view name based on the configured parameters.
	 * @see org.springframework.web.util.UrlPathHelper#getLookupPathForRequest
	 * @see #transformPath
	 */
	@Override
	public String getViewName(HttpServletRequest request) {
		// 1. 获得请求路径
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
		// 2.. 获得视图名
		return (this.prefix + transformPath(lookupPath) + this.suffix);
	}

	/**
	 * Transform the request URI (in the context of the webapp) stripping
	 * slashes and extensions, and replacing the separator as required.
	 * @param lookupPath the lookup path for the current request,
	 * as determined by the UrlPathHelper
	 * @return the transformed path, with slashes and extensions stripped
	 * if desired
	 */
	@Nullable
	protected String transformPath(String lookupPath) {
		String path = lookupPath;
		// 移除开头 SLASH
		if (this.stripLeadingSlash && path.startsWith(SLASH)) {
			path = path.substring(1);
		}
		// 移除末尾 SLASH
		if (this.stripTrailingSlash && path.endsWith(SLASH)) {
			path = path.substring(0, path.length() - 1);
		}
		// 移除拓展名
		if (this.stripExtension) {
			path = StringUtils.stripFilenameExtension(path);
		}
		// 替换分隔符
		if (!SLASH.equals(this.separator)) {
			path = StringUtils.replace(path, SLASH, this.separator);
		}
		return path;
	}

}























 

 














 



 



 



 






2. noView

  • http://localhost:8080/spring_mymvc/noView
    @RequestMapping("/noView")
    public void noView() {
        System.out.println("noView....");
    }
image-20240118164437255

3. noView_user

  • http://localhost:8080/spring_mymvc/noView_user
    @RequestMapping("/noView_user")
    @ResponseBody
    public User noView(ModelMap map) {
        System.out.println("noView_user...");
        User user = new User();
        user.setAge(12);
        user.setName("ooxx");
        return user;
    }
image-20240118165433802

2. applyPostHandle()

// 执行响应的interceptor的postHandler方法
mappedHandler.applyPostHandle(processedRequest, response, mv);

3. processDispatchResult()

  • @ResponseBodymv == null 不会进行 rander()
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * Handle the result of handler selection and handler invocation, which is
     * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {

        // 标记是否为处理生成异常的ModelAndView对象
        boolean errorView = false;

        // 如果请求处理过程中有异常抛出则处理异常
        if (exception != null) { // skip
            // 从ModelAndViewDefiningException中获得ModelAndView对象
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            // 处理异常,生成ModelAndView对象
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                // 2..
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        // @ResponseBody 的 mv == null 不会进行 rander()
        if (mv != null && !mv.wasCleared()) {
            // 1.. 渲染页面
            render(mv, request, response);
            // 清理请求中的错误消息属性
            // 因为上述的情况中,processHandlerException会通过WebUtils设置错误消息属性,所以需要进行清理
            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;
        }

        // 发出请求处理完成通知,触发Interceptor的afterCompletion
        if (mappedHandler != null) {
            // Exception (if any) is already handled..
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

    /**
     * Render the given ModelAndView.
     * <p>This is the last stage in handling a request. It may involve resolving the view by name.
     *
     * @param mv       the ModelAndView to render
     * @param request  current HTTP servlet request
     * @param response current HTTP servlet response
     * @throws ServletException if view is missing or cannot be resolved
     * @throws Exception        if there's a problem rendering the view
     */
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        // 解析request中获得Locale对象,并设置到response中
        Locale locale =
                (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);

        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            // 1.. viewName获得View对象
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                // 获取不到,抛出 ServletException 异常
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        }
        // 直接使用ModelAndView对象的View对象
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                // 获取不到,抛出ServletException
                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());
            }
            // 2... AbstractView 渲染页面
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }

    @Nullable
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
                                   Locale locale, HttpServletRequest request) throws Exception {

        if (this.viewResolvers != null) {
            for (ViewResolver viewResolver : this.viewResolvers) {
                // 1... AbstractCachingViewResolver。根据viewName + locale参数,解析出View对象
                View view = viewResolver.resolveViewName(viewName, locale);
                // 解析成功,直接返回 View 对象
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }

}

























 








 




















 

























 




























 















 










1. InternalResourceViewRsv

image-20240120122859364
public class InternalResourceViewResolver extends UrlBasedViewResolver {

	/**
	 * 判断 javax.servlet.jsp.jstl.core.Config 是否存在
	 */
	private static final boolean jstlPresent = ClassUtils.isPresent(
			"javax.servlet.jsp.jstl.core.Config", InternalResourceViewResolver.class.getClassLoader());

	/**
	 * Sets the default {@link #setViewClass view class} to {@link #requiredViewClass}:
	 * by default {@link InternalResourceView}, or {@link JstlView} if the JSTL API
	 * is present.
	 */
	public InternalResourceViewResolver() {
		// 1.. 获得viewClass
		Class<?> viewClass = requiredViewClass();
		if (InternalResourceView.class == viewClass && jstlPresent) {
			viewClass = JstlView.class;
		}
		// 2... UrlBasedViewResolver 设置viewClass
		setViewClass(viewClass);
	}

	/**
	 * This resolver requires {@link InternalResourceView}.
	 */
	@Override
	protected Class<?> requiredViewClass() {
	    // 1.
		return InternalResourceView.class;
	}

	@Override
	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
		// 1... UrlBasedViewResolver 调用父方法
		InternalResourceView view = (InternalResourceView) super.buildView(viewName);
		if (this.alwaysInclude != null) {
			view.setAlwaysInclude(this.alwaysInclude);
		}
		// 设置 View 对象的相关属性
		view.setPreventDispatchLoop(true);
		return view;
	}

}

















 


 








 





 









1. AbstractCachingViewRsv

public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {

	@Override
	@Nullable
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		// 如果禁用缓存,则创建 viewName 对应的 View 对象
		if (!isCache()) {
			return createView(viewName, locale);
		}
		else {
			// 获得缓存 KEY
			Object cacheKey = getCacheKey(viewName, locale);
			// 从 viewAccessCache 缓存中,获得 View 对象
			View view = this.viewAccessCache.get(cacheKey);
			// 如果获得不到缓存,则从 viewCreationCache 中,获得 View 对象
			if (view == null) {
				synchronized (this.viewCreationCache) {
					// 从 viewCreationCache 中,获得 View 对象
					view = this.viewCreationCache.get(cacheKey);
					if (view == null) {
						// Ask the subclass to create the View object.
						// 1... UrlBasedViewResolver 创建 viewName 对应的 View 对象
						view = createView(viewName, locale);
						// 如果创建失败,但是 cacheUnresolved 为 true ,则设置为 UNRESOLVED_VIEW
						if (view == null && this.cacheUnresolved) {
							view = UNRESOLVED_VIEW;
						}
						// 如果 view 非空,则添加到 viewAccessCache 缓存中
						if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
							this.viewAccessCache.put(cacheKey, view);
							this.viewCreationCache.put(cacheKey, view);
						}
					}
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace(formatKey(cacheKey) + "served from cache");
				}
			}
			return (view != UNRESOLVED_VIEW ? view : null);
		}
	}

	@Nullable
	protected View createView(String viewName, Locale locale) throws Exception {
        // 1... UrlBasedViewResolver
		return loadView(viewName, locale);
	}

}






















 
























 



2. UrlBasedViewRsv

public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {

	/**
	 * Prefix for special view names that specify a redirect URL (usually
	 * to a controller after a form has been submitted and processed).
	 * Such view names will not be resolved in the configured default
	 * way but rather be treated as special shortcut.
	 */
	public static final String REDIRECT_URL_PREFIX = "redirect:";

	/**
	 * Prefix for special view names that specify a forward URL (usually
	 * to a controller after a form has been submitted and processed).
	 * Such view names will not be resolved in the configured default
	 * way but rather be treated as special shortcut.
	 */
	public static final String FORWARD_URL_PREFIX = "forward:";

	/**
	 * View 的类型,不同的实现类,会对应一个 View 的类型
	 */
	@Nullable
	private Class<?> viewClass;

	/**
	 * 是否只处理指定的视图名们
	 */
	@Nullable
	private String[] viewNames;

	protected boolean canHandle(String viewName, Locale locale) {
		// 一般情况下,`viewNames` 指定的视图名们为空,所以会满足 viewNames == null 代码块。所有视图名都可以被处理
		String[] viewNames = getViewNames();
		return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName));
	}

	@Nullable
	protected String[] getViewNames() {
		return this.viewNames;
	}

	/**
	 * Overridden to implement check for "redirect:" prefix.
	 * <p>Not possible in {@code loadView}, since overridden
	 * {@code loadView} versions in subclasses might rely on the
	 * superclass always creating instances of the required view class.
	 * @see #loadView
	 * @see #requiredViewClass
	 */
	@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.
		// 1.. 是否能处理该视图名称
		if (!canHandle(viewName, locale)) {
			return null;
		}

		// Check for special "redirect:" prefix.
		// 2. 如果是 REDIRECT 开头,创建 RedirectView 视图
		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) {
				// 设置 RedirectView 对象的 hosts 属性
				view.setHosts(hosts);
			}
			// 应用
			return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
		}

		// 3. Check for special "forward:" prefix.
		if (viewName.startsWith(FORWARD_URL_PREFIX)) {
			// 如果是 FORWARD 开头,创建 InternalResourceView 视图
			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.
		// 4... AbstractCachingViewResolver 创建视图名对应的 View 对象
		return super.createView(viewName, locale);
	}

	@Override
	protected View loadView(String viewName, Locale locale) throws Exception {
		// 1... InternalResourceViewResolver 创建 viewName 对应的 View 对象
		AbstractUrlBasedView view = buildView(viewName);
		// 2.. lifecycle应用
		View result = applyLifecycleMethods(viewName, view);
		return (view.checkResource(locale) ? result : null);
	}

	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
	    // 1. JstlView
		Class<?> viewClass = getViewClass();
		Assert.state(viewClass != null, "No view class");

		// 2. 创建 AbstractUrlBasedView 对象
		AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
		// 3. 设置各种属性
		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;
	}

	protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {
		// 1. XmlWebApplicationContext 情况一,如果 viewName 有对应的 View Bean 对象,则使用它
		ApplicationContext context = getApplicationContext();
		if (context != null) {
		    // 2. JstlView
			Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);
			if (initialized instanceof View) {
			    // 3.
				return (View) initialized;
			}
		}
		// 情况二,直接返回 view
		return view;
	}

}
































 





















 





 

 







 



 


 

 




 





 

 





 



 

 






























 


 


 







2. AbstractView

image-20240705132203036
public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {

	@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));
		}

		// 1.. 合并返回结果,将 Model 中的静态数据和请求中的动态数据进行合并
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		// 进行一些准备工作(修复 IE 中存在的 BUG)
		prepareResponse(request, response);
		// 2... InternalResourceView, RedirectView 进行渲染
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}

	protected Map<String, Object> createMergedOutputModel(@Nullable Map<String, ?> model,
			HttpServletRequest request, HttpServletResponse response) {

		// 1. 获取请求中一些参数属性
		@SuppressWarnings("unchecked")
		Map<String, Object> pathVars = (this.exposePathVariables ?
				(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);

		// Consolidate static and dynamic model attributes.
		int size = this.staticAttributes.size();
		size += (model != null ? model.size() : 0);
		size += (pathVars != null ? pathVars.size() : 0);

		Map<String, Object> mergedModel = new LinkedHashMap<>(size);
		mergedModel.putAll(this.staticAttributes);
		if (pathVars != null) {
			mergedModel.putAll(pathVars);
		}
		if (model != null) {
			mergedModel.putAll(model);
		}

		// Expose RequestContext?
		if (this.requestContextAttribute != null) {
			mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
		}

		return mergedModel;
    }

	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);
			}
		});
	}

}













 



 








 


























 
 
 
 
 
 
 
 



1. JstView

image-20240120125726547
public class JstlView extends InternalResourceView {

	/**
	 * Exposes a JSTL LocalizationContext for Spring's locale and MessageSource.
	 * @see JstlUtils#exposeLocalizationContext
	 */
	@Override
	protected void exposeHelpers(HttpServletRequest request) throws Exception {
		if (this.messageSource != null) {
			JstlUtils.exposeLocalizationContext(request, this.messageSource);
		}
		else {
		    // 1.
			JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));
		}
	}

	protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
        // 1. `/WEB-INF/jsp/noView.jsp`
		String path = getUrl();
		Assert.state(path != null, "'url' not set");

		if (this.preventDispatchLoop) {
		    // 2. `/spring_mymvc/noView`
			String uri = request.getRequestURI();
			if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {
				throw new ServletException("Circular view path [" + path + "]: would dispatch back " +
						"to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +
						"(Hint: This may be the result of an unspecified view, due to default view name generation.)");
			}
		}
		return path;
	}

}













 






 




 










1. InternalResourceView
public class InternalResourceView extends AbstractUrlBasedView {

	/**
	 * Render the internal resource given the specified model.
	 * This includes setting the model as request attributes.
	 */
	@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
        // 1... AbstractView
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		// 2... JstlView 往请求中设置一些属性,Locale、TimeZone、LocalizationContext
		exposeHelpers(request);

		// Determine the path for the request dispatcher.
		// 3... JstlView 获取需要转发的路径
		String dispatcherPath = prepareForRendering(request, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		// 4.. 获取请求转发器
		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)) { // skip
			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() + "]");
			}
			// 5. Tomcat代码。最后进行转发
			rd.forward(request, response);
		}
	}

	@Nullable
	protected RequestDispatcher getRequestDispatcher(HttpServletRequest request, String path) {
		return request.getRequestDispatcher(path);
	}

}












 



 



 



 



















 





 



2. RedirectView

public class RedirectView extends AbstractUrlBasedView implements SmartView {

	private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");

	@Nullable
	private String[] hosts;

	/**
	 * Convert model to request parameters and redirect to the given URL.
	 * @see #appendQueryProperties
	 * @see #sendRedirect
	 */
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		String targetUrl = createTargetUrl(model, request); // userList
		targetUrl = updateTargetUrl(targetUrl, model, request, response);

		// Save flash attributes
		RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);

		// 1.. Redirect
		sendRedirect(request, response, targetUrl, this.http10Compatible);
	}

	protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
			String targetUrl, boolean http10Compatible) throws IOException {
        // 1. userList
		String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
		if (http10Compatible) {
			HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);
			if (this.statusCode != null) {
				response.setStatus(this.statusCode.value());
				response.setHeader("Location", encodedURL);
			}
			else if (attributeStatusCode != null) {
				response.setStatus(attributeStatusCode.value());
				response.setHeader("Location", encodedURL);
			}
			else {
				// 2. Send status code 302 by default.
				response.sendRedirect(encodedURL);
			}
		}
		else {
			HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
			response.setStatus(statusCode.value());
			response.setHeader("Location", encodedURL);
		}
	}

}




















 


 





 












 










3. triggerAfterCompletion()

4. processHandlerException()

  • http://localhost:8080/spring_mymvc/nullPointer
@Controller
public class ExceptionCtl {

    @RequestMapping("/classNotFound")
    public ModelAndView classNotFoundTest(ModelAndView view) throws ClassNotFoundException {
        view.setViewName("index");
        throw new ClassNotFoundException("ClassNotFoundException。。。。。。");
    }

    @RequestMapping("/nullPointer")
    public ModelAndView nullPointerTest(ModelAndView view) {
        view.setViewName("index");
        throw new NullPointerException("空指针啦。。。。。。");
    }

    // ------------------------------------------------------------------------------------

    @ExceptionHandler(NullPointerException.class)
    public ModelAndView nullPointerException(NullPointerException e) {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("error");
        mav.addObject("msg", e.getMessage());
        return mav;
    }

    @ExceptionHandler(RuntimeException.class)
    public ModelAndView runtimeException(RuntimeException e, HttpServletRequest request) {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("error");
        mav.addObject("msg", e.getMessage());
        return mav;
    }

    @ExceptionHandler
    public ModelAndView allException(Exception e, HttpServletRequest req, HttpServletResponse resp) {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("error");
        mav.addObject("msg", e.getMessage());
        return mav;
    }

}

1. DispatcherServlet

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * 异常处理器,用于解析处理器发生的异常
     * List of HandlerExceptionResolvers used by this servlet.
     */
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    @Nullable
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                                   @Nullable Object handler, Exception ex) throws Exception {

        // Success and error responses may use different content types
        // 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性
        request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

        // Check registered HandlerExceptionResolvers...
        // 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象
        ModelAndView exMv = null;
        if (this.handlerExceptionResolvers != null) {
            // 遍历 HandlerExceptionResolver 数组
            for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
                // 1... AbstractHandlerExceptionResolver 解析异常,生成 ModelAndView 对象
                exMv = resolver.resolveException(request, response, handler, ex);
                // 生成成功,结束循环
                if (exMv != null) {
                    break;
                }
            }
        }
        // 2. 情况一,生成了 ModelAndView 对象,进行返回
        if (exMv != null) {
            // ModelAndView 对象为空,则返回 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);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Using resolved error view: " + exMv);
            }
            // 设置请求中的错误消息属性
            WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
            return exMv;
        }
        // 3. 未生成 ModelAndView 对象,则抛出异常
        throw ex;
    }

}

























 






 





























2. AbsHandlerExceptionResolver

image-20240702150937488
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
 
 

public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {

	@Override
	@Nullable
	public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		// 1... AbstractHandlerMethodExceptionResolver 都可应用
		if (shouldApplyTo(request, handler)) {
			// 阻止缓存
			prepareResponse(ex, response);
			// 2... AbstractHandlerMethodExceptionResolver, ResponseStatusExceptionResolver 执行解析异常,返回modelAndView对象
			ModelAndView result = doResolveException(request, response, handler, ex);
			// 如果ModelAndView对象非空,则进行返回
			if (result != null) {
				// Print debug message when warn logger is not enabled.
				if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
					logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
				}
				// Explicitly configured warn logger in logException method.
				logException(ex, request);
			}
			return result;
		}
		// 不可应用,直接返回null
		else {
			return null;
		}
	}

	protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
		if (handler != null) {
			// 如果mappedHandler包含handler对象,则返回true
			if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
				return true;
			}
			// 如果mappedHandlerClasses包含handler类型,则返回true
			if (this.mappedHandlerClasses != null) {
				for (Class<?> handlerClass : this.mappedHandlerClasses) {
					if (handlerClass.isInstance(handler)) {
						return true;
					}
				}
			}
		}
		// Else only apply if there are no explicit handler mappings.
		// 1. 如果mappedHandlers和mappedHandlerClasses都为空,说明直接匹配
		return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
	}

}








 



 


































 



1. ExceptionHandlerExceptionRsv
image-20240219111024677
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {

	/**
	 * Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception.
	 */
	@Override
	@Nullable
	protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

		// 1.. 获得异常对应的 ServletInvocableHandlerMethod 对象,handlerMethod是处理异常的方法
		ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
		if (exceptionHandlerMethod == null) {
			return null;
		}

		// 2. 设置 ServletInvocableHandlerMethod 对象的相关属性,主要是为了处理HandlerMethod中方法的参数以及handlerMethod的返回值
		if (this.argumentResolvers != null) {
			exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}

		// 创建 ServletWebRequest 对象
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		// 创建 ModelAndViewContainer 对象
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();

		try {
			if (logger.isDebugEnabled()) {
				logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
			}
			// 执行处理该异常的方法 ServletInvocableHandlerMethod 的调用,主要是对参数和返回值及进行处理,通过ModelAndViewContainer作为中间变量
			// 将一些视图名、参数放到ModelAndViewContainer中
			Throwable cause = exception.getCause();
			if (cause != null) {
				// Expose cause as provided argument as well
				exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
			}
			else {
				// Otherwise, just the given exception as-is
                // 3... ServletInvocableHandlerMethod
				exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
			}
		}
		catch (Throwable invocationEx) {
			// Any other than the original exception (or its cause) is unintended here,
			// probably an accident (e.g. failed assertion or the like).
			// 发生异常,则直接返回
			if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
				logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
			}
			// Continue with default processing of the original exception...
			return null;
		}

		// 如果 mavContainer 已处理,则返回 '空的' ModelAndView 对象。
		if (mavContainer.isRequestHandled()) {
			return new ModelAndView();
		}
		// 4. mavContainer未处理,则基于`mavContainer`生成ModelAndView
		else {
			ModelMap model = mavContainer.getModel();
			HttpStatus status = mavContainer.getStatus();
			// 创建 ModelAndView 对象,并设置相关属性
			ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
			mav.setViewName(mavContainer.getViewName());
			if (!mavContainer.isViewReference()) {
				mav.setView((View) mavContainer.getView());
			}
			if (model instanceof RedirectAttributes) {
				Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
			return mav;
		}
	}

	@Nullable
	protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
			@Nullable HandlerMethod handlerMethod, Exception exception) {

		// 处理器的类型
		Class<?> handlerType = null;

		// 首先,如果 handlerMethod 非空,则先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法
		if (handlerMethod != null) {
			// Local exception handler methods on the controller class itself.
			// To be invoked through the proxy, even in case of an interface-based proxy.
			// 获得 handlerType
			handlerType = handlerMethod.getBeanType();
			// 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象
			ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
			if (resolver == null) {
                // 1... ExceptionHandlerMethodResolver
				resolver = new ExceptionHandlerMethodResolver(handlerType);
				this.exceptionHandlerCache.put(handlerType, resolver);
			}
			// 2... ExceptionHandlerMethodResolver 获得异常对应的Method处理方法
			Method method = resolver.resolveMethod(exception);
			// 如果获得该异常对应的 Method 处理方法,则创建 ServletInvocableHandlerMethod 对象,并返回
			if (method != null) {
                // 3.
				return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
			}
			// For advice applicability check below (involving base packages, assignable types
			// and annotation presence), use target class instead of interface-based proxy.
			// 获得 handlerType 的原始类。因为,此处有可能是代理对象
			if (Proxy.isProxyClass(handlerType)) {
				handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
			}
		}

		// 4. 其次,使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法
		for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
			ControllerAdviceBean advice = entry.getKey();
			// 如果 ControllerAdvice 支持当前的 handlerType
			if (advice.isApplicableToBeanType(handlerType)) {
				// 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象
				ExceptionHandlerMethodResolver resolver = entry.getValue();
				// 获得异常对应的 Method 处理方法
				Method method = resolver.resolveMethod(exception);
				if (method != null) {
					return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
				}
			}
		}

		// 最差,获取不到
		return null;
	}

}












 






 


 





















 






















 





























 



 



 










 
 



 

 

 









1. AbsHandlerMtdExceptionRsv
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {

	@Override
	protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
		// 1. 如果 handler 为空,则直接调用父方法
		if (handler == null) {
			return super.shouldApplyTo(request, null);
		}
		// 2. 处理 handler 为 HandlerMethod 类型的情况
		else if (handler instanceof HandlerMethod) {
			// 获得真正的 handler
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			handler = handlerMethod.getBean();
			// 1... AbstractHandlerExceptionResolver 调用父方法
			return super.shouldApplyTo(request, handler);
		}
		// 3. 直接返回 false
		else {
			return false;
		}
	}

	@Override
	@Nullable
	protected final ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
		// 2... ExceptionHandlerExceptionResolver
		return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
	}

}














 












 



2. ExceptionHandlerMtdResolver
public class ExceptionHandlerMethodResolver {

	/**
	 * A filter for selecting {@code @ExceptionHandler} methods.
	 */
	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);

	private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);

	private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);

	/**
	 * A constructor that finds {@link ExceptionHandler} methods in the given type.
	 * @param handlerType the type to introspect
	 */
	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
		// 1. class及父类中找出@ExceptionHandler注解方法
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
            // 2..
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				// 3.. 以异常Throwable为key,Method为value丢入到map缓存中
				addExceptionMapping(exceptionType, method);
			}
		}
	}

	/**
	 * Extract exception mappings from the {@code @ExceptionHandler} annotation first,
	 * and then as a fallback from the method signature itself.
	 */
	@SuppressWarnings("unchecked")
	private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
		List<Class<? extends Throwable>> result = new ArrayList<>();
		// 1.. 找出方法的@ExceptionHandler注解,将注解中的value数组属性加入到result中
		detectAnnotationExceptionMappings(method, result);
		if (result.isEmpty()) {
			// 如果注解中的value属性没有指定,那么获取方法参数中的异常
			for (Class<?> paramType : method.getParameterTypes()) {
				if (Throwable.class.isAssignableFrom(paramType)) {
					result.add((Class<? extends Throwable>) paramType);
				}
			}
		}
		if (result.isEmpty()) {
			throw new IllegalStateException("No exception types mapped to " + method);
		}
		return result;
	}

	private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
        // 1..
		ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
		Assert.state(ann != null, "No ExceptionHandler annotation");
		result.addAll(Arrays.asList(ann.value()));
	}

	private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
	    // 1.
		Method oldMethod = this.mappedMethods.put(exceptionType, method);
		if (oldMethod != null && !oldMethod.equals(method)) {
			throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
					exceptionType + "]: {" + oldMethod + ", " + method + "}");
		}
	}

	// ---------------------------------------------------------------------------------------------------

	@Nullable
	public Method resolveMethod(Exception exception) {
	    // 1..
		return resolveMethodByThrowable(exception);
	}


	@Nullable
	public Method resolveMethodByThrowable(Throwable exception) {
        // 1..
		Method method = resolveMethodByExceptionType(exception.getClass());
		if (method == null) {
			Throwable cause = exception.getCause();
			if (cause != null) {
				method = resolveMethodByExceptionType(cause.getClass());
			}
		}
		return method;
	}

	@Nullable
	public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
		Method method = this.exceptionLookupCache.get(exceptionType);
		if (method == null) {
            // 1..
			method = getMappedMethod(exceptionType);
			this.exceptionLookupCache.put(exceptionType, method);
		}
		return method;
	}

	/**
	 * Return the {@link Method} mapped to the given exception type, or {@code null} if none.
	 */
	@Nullable
	private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
		List<Class<? extends Throwable>> matches = new ArrayList<>();
		for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
            // 1..
			if (mappedException.isAssignableFrom(exceptionType)) {
				matches.add(mappedException);
			}
		}
		if (!matches.isEmpty()) {
            // 2. 按从小到大排序
			matches.sort(new ExceptionDepthComparator(exceptionType));
			return this.mappedMethods.get(matches.get(0));
		}
		else {
			return null;
		}
	}

}






 











 

 

 












 




 











 






 

 









 






 














 











 

 





 
 







2. ResponseStatusExceptionRsv
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {

	@Override
	@Nullable
	protected ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		try {
			// 1.. 情况一,如果异常是 ResponseStatusException 类型,进行解析并设置到响应
			if (ex instanceof ResponseStatusException) {
				return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
			}

			// 2. 情况二,如果有 @ResponseStatus 注解,进行解析并设置到响应
			ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
			if (status != null) {
                // 3..
				return resolveResponseStatus(status, request, response, handler, ex);
			}
			// 4.. 情况三,使用异常的 cause 在走一次情况一、情况二的逻辑。
			if (ex.getCause() instanceof Exception) {
				return doResolveException(request, response, handler, (Exception) ex.getCause());
			}
		}
		catch (Exception resolveEx) {
			if (logger.isWarnEnabled()) {
				logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
			}
		}
		return null;
	}

	protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
			HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {

		int statusCode = responseStatus.code().value();
		String reason = responseStatus.reason();
		// 1..
		return applyStatusAndReason(statusCode, reason, response);
	}

	protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
			throws IOException {

		// 情况一,如果无错误提示,则响应只设置状态码
		if (!StringUtils.hasLength(reason)) {
			response.sendError(statusCode);
		}
		// 情况二,如果有错误信息,则响应设置状态码 + 错误提示
		else {
			// 进一步解析错误提示,如果有 messageSource 的情况下
			String resolvedReason = (this.messageSource != null ?
					this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
					reason);
			// 1. 设置
			response.sendError(statusCode, resolvedReason);
		}
		// 2. 创建“空” ModelAndView 对象,并返回
		return new ModelAndView();
	}

	protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception {

        // 1.
		ex.getResponseHeaders().forEach((name, values) ->
				values.forEach(value -> response.addHeader(name, value)));

		int statusCode = ex.getStatus().value();
		String reason = ex.getReason();
		// 2..
		return applyStatusAndReason(statusCode, reason, response);
	}

}










 



 


 



 
















 
















 


 







 




 



1. HttpStatus
public enum HttpStatus {

    // 1xx Informational

    // 2xx Success

    /**
     * {@code 200 OK}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.3.1">HTTP/1.1: Semantics and Content, section 6.3.1</a>
     */
    OK(200, "OK"),

    // 3xx Redirection

    // --- 4xx Client Error ---

    /**
     * {@code 400 Bad Request}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.5.1">HTTP/1.1: Semantics and Content, section 6.5.1</a>
     */
    BAD_REQUEST(400, "Bad Request"),

    /**
     * {@code 401 Unauthorized}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7235#section-3.1">HTTP/1.1: Authentication, section 3.1</a>
     */
    UNAUTHORIZED(401, "Unauthorized"),

    /**
     * {@code 402 Payment Required}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.5.2">HTTP/1.1: Semantics and Content, section 6.5.2</a>
     */
    PAYMENT_REQUIRED(402, "Payment Required"),

    /**
     * {@code 403 Forbidden}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.5.3">HTTP/1.1: Semantics and Content, section 6.5.3</a>
     */
    FORBIDDEN(403, "Forbidden"),

    /**
     * {@code 404 Not Found}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.5.4">HTTP/1.1: Semantics and Content, section 6.5.4</a>
     */
    NOT_FOUND(404, "Not Found"),

    // --- 5xx Server Error ---

    /**
     * {@code 500 Internal Server Error}.
     *
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-6.6.1">HTTP/1.1: Semantics and Content, section 6.6.1</a>
     */
    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),

}











 










 




































 


2. @ResponseStatus
@ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "ooxxRunException")
public class MyRuntimeException extends RuntimeException {
    private static final long serialVersionUID = -7876639844077632100L;

    public MyRuntimeException() {
    }

    public MyRuntimeException(String message) {
        super(message);
    }

}
 











@Controller
public class MyRuntimeCtl {

    @RequestMapping("/myRunException")
    public ModelAndView myRunException(ModelAndView view) {
        view.setViewName("index");
        throw new MyRuntimeException("MyRunException。。。。。。");
    }

}






 



image-20240121093710637
3. DefaultHandlerExceptionRsv
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {

	@Override
	@Nullable
	protected ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		try {
			if (ex instanceof HttpRequestMethodNotSupportedException) {
				return handleHttpRequestMethodNotSupported(
						(HttpRequestMethodNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMediaTypeNotSupportedException) {
				return handleHttpMediaTypeNotSupported(
						(HttpMediaTypeNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMediaTypeNotAcceptableException) {
				return handleHttpMediaTypeNotAcceptable(
						(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
			}
			else if (ex instanceof MissingPathVariableException) {
				return handleMissingPathVariable(
						(MissingPathVariableException) ex, request, response, handler);
			}
			else if (ex instanceof MissingServletRequestParameterException) {
				return handleMissingServletRequestParameter(
						(MissingServletRequestParameterException) ex, request, response, handler);
			}
			else if (ex instanceof ServletRequestBindingException) {
				return handleServletRequestBindingException(
						(ServletRequestBindingException) ex, request, response, handler);
			}
			else if (ex instanceof ConversionNotSupportedException) {
				return handleConversionNotSupported(
						(ConversionNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof TypeMismatchException) {
				return handleTypeMismatch(
						(TypeMismatchException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotReadableException) {
				return handleHttpMessageNotReadable(
						(HttpMessageNotReadableException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotWritableException) {
				return handleHttpMessageNotWritable(
						(HttpMessageNotWritableException) ex, request, response, handler);
			}
			else if (ex instanceof MethodArgumentNotValidException) {
				return handleMethodArgumentNotValidException(
						(MethodArgumentNotValidException) ex, request, response, handler);
			}
			else if (ex instanceof MissingServletRequestPartException) {
				return handleMissingServletRequestPartException(
						(MissingServletRequestPartException) ex, request, response, handler);
			}
			else if (ex instanceof BindException) {
				return handleBindException((BindException) ex, request, response, handler);
			}
			else if (ex instanceof NoHandlerFoundException) {
                // 1..
				return handleNoHandlerFoundException(
						(NoHandlerFoundException) ex, request, response, handler);
			}
			else if (ex instanceof AsyncRequestTimeoutException) {
				return handleAsyncRequestTimeoutException(
						(AsyncRequestTimeoutException) ex, request, response, handler);
			}
		}
		catch (Exception handlerEx) {
			if (logger.isWarnEnabled()) {
				logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
			}
		}
		return null;
	}

	protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

		pageNotFoundLogger.warn(ex.getMessage());
		// 2. 404
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
		return new ModelAndView();
	}

}





























































 
 



















 




1. HttpServletResponse
package javax.servlet.http;

public interface HttpServletResponse extends ServletResponse {
    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

}
























 





















2. NoHandlerFoundException
@Controller
public class DefaultCtl {

    @RequestMapping("/defaultHandlerException")
    public ModelAndView defaultHandlerException(ModelAndView view, HttpServletRequest request) throws NoHandlerFoundException {
        view.setViewName("error");
        throw new NoHandlerFoundException(null, null, null);
    }

}
4. MyExHandlerExResolver
<bean class="com.listao.exception.MyExceptionHandlerExceptionResolver">
    <property name="order" value="-1"/>
</bean>
public class MyExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver {

    // 通过构造方法的方式来添加一个参数处理器
    public MyExceptionHandlerExceptionResolver() {
        List<HandlerMethodArgumentResolver> list = new ArrayList<>();
        list.add(new ServletModelAttributeMethodProcessor(true));
        this.setCustomArgumentResolvers(list);
    }

    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
                                                           HandlerMethod handlerMethod, Exception exception) {

        // 找到对应的异常处理的方法
        ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
        // 判断找到的异常处理方式是否为空,如果为空,直接返回
        if (exceptionHandlerMethod == null) {
            return null;
        }

        // 在本身自带的逻辑中,参数处理器没有办法处理我们对应的参数,所以此时就要思考如何添加一个参数处理器进来
        // 我们在定义一个正常方法的时候,能否传递一个ModelAndView的对象?可以
        exceptionHandlerMethod.setDataBinderFactory(new ServletRequestDataBinderFactory(null, null));

        // 设置参数处理器,返回值处理器
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers());
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(getReturnValueHandlers());


        // 创建 ServletWebRequest 对象
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        // 创建 ModelAndViewContainer 对象
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();

        try {
            if (logger.isDebugEnabled()) {
                logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
            }
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
        } catch (Throwable invocationEx) {
            // Any other than the original exception (or its cause) is unintended here,
            // probably an accident (e.g. failed assertion or the like).
            // 发生异常,则直接返回
            if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
                logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
            }
            // Continue with default processing of the original exception...
            return null;
        }

        // 如果 mavContainer 已处理,则返回 '空的' ModelAndView 对象。
        if (mavContainer.isRequestHandled()) {
            return new ModelAndView();
        }
        // 如果 mavContainer 未处,则基于 `mavContainer` 生成 ModelAndView 对象
        else {
            ModelMap model = mavContainer.getModel();
            HttpStatus status = mavContainer.getStatus();
            // 创建 ModelAndView 对象,并设置相关属性
            ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
            mav.setViewName(mavContainer.getViewName());
            if (!mavContainer.isViewReference()) {
                mav.setView((View) mavContainer.getView());
            }
            if (model instanceof RedirectAttributes) {
                Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
                RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }
            return mav;
        }
    }

}