《看透springMvc源代码分析与实践》学习笔记
SpringMVC 版本 4.1.5.RELEASE
HandlerExceptionResolver
HandlerExceptionResolver
主要用于解析处理过程中产生的异常。
HandlerExceptionResolver 类图
其中HandlerExceptionResolverComposite
作为容器,可以封装别的HandlerExceptionResolver,跟前面的XXXResolverComposite类似。
HandlerExceptionResolver的主要实现类都是继承自AbstractHandlerExceptionResolver
,它有五个子类。
AnnotationMethodHandlerExceptionResolver
已经废弃。AbstractHandlerMethodExceptionResolver
和其子类ExceptionHandlerExceptionResolver
:一起完成对@ExceptionHandler
注释的方法进行异常解析。DefaultHandlerExceptionResolver
: 根据各个不同类型的异常,返回不同的异常视图ResponseStatusExceptionResolver
: 解析带有@ResponseStatus
注释类型的异常SimpleMappingExceptionResolver
: 通过配置异常类和view的关系来解析异常
异常解析过程中可以做如下工作:给mav设置相应的内容、设置response相关属性,还可以做一些辅助工作:如记录日志等。
AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver
是所有解析异常类的父类,里面定义了通用的解析流程,并使用模板方法,子类只需覆盖相应的方法即可。
//org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
//用于解析哪些handler抛出的异常
private Set<?> mappedHandlers;
//用于解析哪些handlerClass抛出的异常
private Class<?>[] mappedHandlerClasses;
private boolean preventResponseCaching = false;
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {
//判断是否解析handler
if (shouldApplyTo(request, handler)) {
//记录异常日志
logException(ex, request);
//禁止response缓存: 默认false
prepareResponse(ex, response);
//模板方法doResolveException,由子类实现
return doResolveException(request, response, handler, ex);
} else {
return null;
}
}
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
if ( handler!= null) {
//如果配置了mappedHandlers,则判断是否包含
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
//如果配置了mappedHandlerClasses,同样规则判断
if (this.mappedHandlerClasses != null) {
for (Class<?> handlerClass : this.mappedHandlerClasses) {
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
//如果二者都没有设置值,则恒为true,即解析所有handler
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
}
}
AbstractHandlerMethodExceptionResolver 和 ExceptionHandlerExceptionResolver
AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver
重写了AbstractHandlerExceptionResolver的shouldApplyTo
和doResolveException
方法:
//org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver
@Override
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
if (handler == null) {
return super.shouldApplyTo(request, handler);
}else if (handler instanceof HandlerMethod) {
//只允许解析 HandlerMethod 抛出的异常
HandlerMethod handlerMethod = (HandlerMethod) handler;
handler = handlerMethod.getBean();
return super.shouldApplyTo(request, handler);
} else {
return false;
}
}
@Override
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
//模板方法,由子类实现
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver
从代码上看,它是一个简化版本的RequestMappingHandlerAdapter
。
初始化
ExceptionHandlerExceptionResolver
同RequestMappingHandlerAdapter
类似,实现了InitializingBean
接口,它的初始化工作在afterPropertiesSet
内完成,
而在afterPropertiesSet
主要的处理逻辑调用了initExceptionHandlerAdviceCache
方法,initExceptionHandlerAdviceCache
代码如下:
//org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<Class<?>, ExceptionHandlerMethodResolver>(64);
//记录@ControllerAdvice中 ExceptionHandlerMethodResolver 信息
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =
new LinkedHashMap<ControllerAdviceBean, ExceptionHandlerMethodResolver>();
private void initExceptionHandlerAdviceCache() {
//if部分...略...
//寻找所有带有@ControllerAdvice注解的bean
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
Collections.sort(adviceBeans, new OrderComparator());
for (ControllerAdviceBean adviceBean : adviceBeans) {
//处理adviceBean类中,带有@ExceptionHandler 注解的方法
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
this.responseBodyAdvice.add(adviceBean);
}
}
}
}
ExceptionHandlerMethodResolver
这里重点关注下ExceptionHandlerMethodResolver
//org.springframework.web.method.annotation.ExceptionHandlerMethodResolver{
public class ExceptionHandlerMethodResolver {
//寻找带有@ExceptionHandler 注解的方法
public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
}
};
//记录异常与处理异常方法映射关系
private final Map<Class<? extends Throwable>, Method> mappedMethods = new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
//HandlerMethodSelector.selectMethods :: 寻找带有@ExceptionHandler 注解的方法
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
//detectExceptionMappings:: @ExceptionHandler注解的方法中 配置的Throwable类型的值(array)
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
//addExceptionMapping::
addExceptionMapping(exceptionType, method);
}
}
}
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
//寻找注解中配置的 Throwable
detectAnnotationExceptionMappings(method, result);
//如果注解中配置的异常为空,则寻找方法入参中的 Throwable 类型参数
if (result.isEmpty()) {
for (Class<?> paramType : method.getParameterTypes()) {
if (Throwable.class.isAssignableFrom(paramType)) {
result.add((Class<? extends Throwable>) paramType);
}
}
}
//如果result 为空,则抛出异常
Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
return result;
}
//寻找带有@ExceptionHandler注解的方法
protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
ExceptionHandler annot = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
result.addAll(Arrays.asList(annot.value()));
}
//如果同一个异常有多个method处理,则抛出异常
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException(
"Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
oldMethod + ", " + method + "}.");
}
}
}
处理异常
处理异常的逻辑是在doResolveHandlerMethodException
中处理的, 它的代码如下:
//org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
//找到处理异常的方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
//设置argumentResolvers 和 returnValueHandlers
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
}
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
//找到 执行方法对应的 handlerType
Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
if (handlerMethod != null) {
//获取执行方法对应的handlerType ,配置的 ExceptionHandlerMethodResolver信息
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
//查找 resolver 是否处理该类型的异常,如果有,则返回处理该异常的方法
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
//从exceptionHandlerAdviceCache缓存信息,处理
for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
DefaultHandlerExceptionResolver
DefaultHandlerExceptionResolver
的解析过程,是根据不同类型的异常,进行不同的处理。
//org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
try {
if (ex instanceof NoSuchRequestHandlingMethodException) {
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
handler);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
response, handler);
}
//略... 其他类型异常
else if (ex instanceof NoHandlerFoundException) {
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
}
}
catch (Exception handlerException) {
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return null;
}
//handleXXXException : 处理异常的逻辑是给response设置相应的属性,sendError方法会根据errorCode跳转至web.xml定义的errorPages.
protected ModelAndView handleNoHandlerFoundException(MissingServletRequestParameterException ex,
HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
}
}
ResponseStatusExceptionResolver
ResponseStatusExceptionResolver
用来解析注释了@ResponseStatus
的异常。
通常使用自定义异常类,并在该类上增加@ResponseStatus.
//org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
//查看该异常类上是否带有 @ResponseStatus 注解
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
return resolveResponseStatus(responseStatus, request, response, handler, ex);
}
return null;
}
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.value().value();
String reason = responseStatus.reason();
if (this.messageSource != null) {
reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
}
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
} else {
response.sendError(statusCode, reason);
}
return new ModelAndView();
}
}
SimpleMappingExceptionResolver
SimpleMappingExceptionResolver
需要提前配置异常类和view的对应关系后,才能使用,代码如下:
//org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
//用来配置异常类与viewName的对应关系
private Properties exceptionMappings;
//配置 不处理的异常类
private Class<?>[] excludedExceptions;
//当无法从exceptionMappings解析出视图时,使用more视图
private String defaultErrorView;
//解析出的视图与状态码之间的关系
private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
//默认状态码
private Integer defaultStatusCode;
//异常信息保存到Model中的参数名, 默认为 "exception"
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
//根据异常查找 显示错误的页面
String viewName = determineViewName(ex, request);
if (viewName != null) {
//从statusCodes中提取配置的viewName的对应的statusCode
Integer statusCode = determineStatusCode(request, viewName);
if (statusCode != null) {
applyStatusCodeIfPossible(request, response, statusCode);
}
//返回ModelAndView ,并将ex信息设置到Model的DEFAULT_EXCEPTION_ATTRIBUTE 中
return getModelAndView(viewName, ex, request);
} else {
return null;
}
}
protected String determineViewName(Exception ex, HttpServletRequest request) {
String viewName = null;
//判断是否在排除的异常中
if (this.excludedExceptions != null) {
for (Class<?> excludedEx : this.excludedExceptions) {
if (excludedEx.equals(ex.getClass())) {
return null;
}
}
}
// 寻找配置的 异常-viewName
if (this.exceptionMappings != null) {
//根据一定的规则,选择最优匹配viewName
viewName = findMatchingViewName(this.exceptionMappings, ex);
}
//如果没有匹配到,则返回默认的defaultErrorView
if (viewName == null && this.defaultErrorView != null) {
viewName = this.defaultErrorView;
}
return viewName;
}
}