1 概述
在Web请求处理的过程中,如果发生异常,就需要对异常进行处理。异常可能是各种各样的,有些需要特殊处理,有些通用处理即可。这些处理的逻辑如果到处放,维护起来也比较困难,Spring提供的异常处理机制考虑了这些问题,本文了解一下其原理。
2 原理
2.1 配置HandlerExceptionResolver
开启@EnableWebMvc的时候,会初始化HandlerExceptionResolver:
- 初始化的时候,先通过WebMvcConfigurer的configureHandlerExceptionResolvers配置自定义的HandlerExceptionResolver;
- 如果没有配置则提供默认的三个HandlerExceptionResolver:ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver。默认配置的和通过WebMvcConfigurer的configureHandlerExceptionResolvers配置的属于二选一,而不是合并起来。
- 再通过WebMvcConfigurer的extendHandlerExceptionResolvers增加额外的HandlerExceptionResolver,这个是额外增加的,不影响之前添加的。
- 把所有HandlerExceptionResolver设置到HandlerExceptionResolverComposite,它也实现了HandlerExceptionResolver接口,并成为Spring的一个bean,这样可以通过接口查找到这个bean,方便注入。
// 源码位置:org.springframework.web.servlet.config.annotation.EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
// 源码位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
// 1. 配置ExceptionResolver
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
// 源码位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
// 2. configurers为WebMvcConfigurerComposite,里面注入了实现了WebMvcConfigurer接口的所有类
this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
}
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
for (WebMvcConfigurer delegate : this.delegates) {
// 3. 遍历所有的WebMvcConfigurer,执行它的configureHandlerExceptionResolvers,把得到的HandlerExceptionResolver加到exceptionResolvers列表里
delegate.configureHandlerExceptionResolvers(exceptionResolvers);
}
}
// 回到WebMvcConfigurationSupport的handlerExceptionResolver继续处理
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
// 1. 配置ExceptionResolver
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
// 4. 如果WebMvcConfigurer没有提供HandlerExceptionResolver,则添加默认的HandlerExceptionResolver
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
extendHandlerExceptionResolvers(exceptionResolvers);
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers);
return composite;
}
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) {
// 5. 添加ExceptionHandlerExceptionResolver,并把ArgumentResolver、ReturnValueHandler、MessageConverter等组装进去,相当于要处理一个请求的形式
ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
exceptionHandlerResolver.setMessageConverters(getMessageConverters());
exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
if (this.applicationContext != null) {
exceptionHandlerResolver.setApplicationContext(this.applicationContext);
}
exceptionHandlerResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerResolver);
// 6. 添加ResponseStatusExceptionResolver
ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
responseStatusResolver.setMessageSource(this.applicationContext);
exceptionResolvers.add(responseStatusResolver);
// 7. 添加DefaultHandlerExceptionResolver
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}
// 回到WebMvcConfigurationSupport的handlerExceptionResolver继续处理
// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
// 1. 配置ExceptionResolver
configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
// 4. 如果WebMvcConfigurer没有提供HandlerExceptionResolver,则添加默认的HandlerExceptionResolver
addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
}
// 8. WebMvcConfigurer还可以通过extendHandlerExceptionResolvers配置exceptionResolver
extendHandlerExceptionResolvers(exceptionResolvers);
// 9. HandlerExceptionResolverComposite也实现HandlerExceptionResolver接口,它成为一个可以查找的bean,里面带了上面配置的所有exceptionResolver
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
composite.setOrder(0);
composite.setExceptionResolvers(exceptionResolvers); // 设置exceptionResolver
return composite;
}
2.2 处理异常流程
客户端发起的http请求由DispatcherServlet处理,DispatcherServlet初始化的时候把上面初始化的ExceptionResolver对应的bean都取到,然后在处理请求的时候,通过ExceptionResolver进行异常处理。
// DispatcherServlet初始化的时候把HandlerExceptionResolver对应的bean都找到放handlerExceptionResolvers里
// 源码位置:org.springframework.web.servlet.DispatcherServlet
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// 1. 根据HandlerExceptionResolver接口获取实现了该接口的bean,如果没有自定义额外的bean,则默认能找到两个:
// org.springframework.boot.web.servlet.error.DefaultErrorAttributes
// org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
// 注意:通过WebMvcConfigurer配置的并不是bean,它配置的HandlerExceptionResolver是存到HandlerExceptionResolverComposite里的
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
// 2. 赋值给handlerExceptionResolvers,供后面处理异常的时候使用
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
// 3. detectAllHandlerExceptionResolvers默认为true,如果关掉了,需要提供一个名称为handlerExceptionResolver的HandlerExceptionResolver
// HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"
HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
// DispatcherServlet在处理http请求的时候,从handlerExceptionResolvers里取出HandlerExceptionResolver进行异常处理
// 源码位置:org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
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) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 4. 处理请求的结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// 省略部分代码
}
// 源码位置:org.springframework.web.servlet.DispatcherServlet
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// 5. 如果有异常,则把异常处理成一个结果
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
// 源码位置:org.springframework.web.servlet.DispatcherServlet
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
// 6. 遍历所有ExceptionResolver,执行通用方法resolveException,只要执行到一个有返回值的,则忽略后面的ExceptionResolver
// handlerExceptionResolvers是上面DispatcherServlet初始化的时候赋值的,默认有两个:
// org.springframework.boot.web.servlet.error.DefaultErrorAttributes
// org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
// 重点看一下HandlerExceptionResolverComposite的处理,它里面设置了所有配置的非bean的ExceptionResolver
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
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;
}
throw ex;
}
// 源码位置:org.springframework.web.servlet.handler.HandlerExceptionResolverComposite
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
// 7. 遍历包装的HandlerExceptionResolver,执行resolveException()方法,默认包装了3个,主要看一下ExceptionHandlerExceptionResolver:
// org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
// org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
// org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
if (this.resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (mav != null) {
return mav;
}
}
}
return null;
}
// 继承关系:ExceptionHandlerExceptionResolver < AbstractHandlerMethodExceptionResolver < AbstractHandlerExceptionResolver
// resolveException()由AbstractHandlerExceptionResolver提供,ExceptionHandlerExceptionResolver重载了doResolveException()方法
// org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
// 8. 处理异常
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
}
logException(ex, request);
}
return result;
}
else {
return null;
}
}
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 9. 获取处理异常的方法,handlerMethod对应的是Controller接收请求的Method
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 省略部分代码
}
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
if (handlerMethod != null) {
// 10. 取到Controller的类
handlerType = handlerMethod.getBeanType();
// 11. 根据Controller类从缓存exceptionHandlerCache里取ExceptionHandlerMethodResolver
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
// 12. 如果exceptionHandlerCache没有取到ExceptionHandlerMethodResolver,则创建一个
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
}
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
}
}
}
return null;
}
// 源码位置:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver
public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
// 13. EXCEPTION_HANDLER_METHODS为一个function,主要是过滤是否有@ExceptionHandler注解,
// 即遍历Controller里的方法,把标注了@ExceptionHandler注解的方法过滤出来
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method); // 过滤出的结果放到this.mappedMethods
}
}
}
// 回到ExceptionHandlerExceptionResolver的getExceptionHandlerMethod()继续处理
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
if (handlerMethod != null) {
// 10. 取到Controller的类
handlerType = handlerMethod.getBeanType();
// 11. 根据Controller类从缓存exceptionHandlerCache里取ExceptionHandlerMethodResolver
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
// 12. 如果exceptionHandlerCache没有取到ExceptionHandlerMethodResolver,则创建一个
resolver = new ExceptionHandlerMethodResolver(handlerType);
// 14. 把新创建的ExceptionHandlerMethodResolver的缓存起来,避免每次请求都需要重复创建
this.exceptionHandlerCache.put(handlerType, resolver);
}
// 15. @ExceptionHandler注解需要指定一种Exception类型,需要根据具体的异常匹配到具体的method
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
}
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
// 16. ExceptionHandlerExceptionResolver初始化的时候initExceptionHandlerAdviceCache(),会把@ControllerAdvice标注的类里的方法也作为处理异常的方法
// 通过具体的异常匹配到处理异常的方法,原理和上面一样
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
}
}
}
return null;
}
private void initExceptionHandlerAdviceCache() {
// 省略部分代码
// 17. 如果类上标注了@ControllerAdvice,这个类也会被Spring实例化为bean,也需要用ExceptionHandlerMethodResolver从这些类里找到对应处理异常的方法
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
// 省略部分代码
}
// 回到ExceptionHandlerExceptionResolver的doResolveHandlerMethodException()继续处理
// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 9. 获取处理异常的方法,handlerMethod对应的是Controller接收请求的Method
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 10. 给ServletInvocableHandlerMethod设置argumentResolvers、returnValueHandlers等,并调用实际方法处理异常
// 可以看作:当调用Controller的一个业务方法service1()产生异常的时候,相当于执行的是对应的异常处理方法exception1(),
// 两个方法的过程基本一样:处理参数、调用方法执行逻辑、处理返回值等。
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
ArrayList<Throwable> exceptions = new ArrayList<>();
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable exToExpose = exception;
while (exToExpose != null) {
exceptions.add(exToExpose);
Throwable cause = exToExpose.getCause();
exToExpose = (cause != exToExpose ? cause : null);
}
Object[] arguments = new Object[exceptions.size() + 1];
exceptions.toArray(arguments);
arguments[arguments.length - 1] = handlerMethod;
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
}
catch (Throwable invocationEx) {
if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
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;
}
}
2.3 使用
SpringMVC在处理Http请求的时候,留了两种方式供介入异常处理:
1) 自行定义HandlerExceptionResolver,需实现HandlerExceptionResolver接口
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
然后用WebMvcConfigurer的下面接口把它注入到Spring中:
public interface WebMvcConfigurer {
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
}
注意:
- 如果configureHandlerExceptionResolvers()提供了HandlerExceptionResolver,那么默认的HandlerExceptionResolver就没有了,需要自行处理所有的异常。如果只是针对某几种场景的异常希望自行处理,需要通过extendHandlerExceptionResolvers()提供。
- 处理请求的时候,只会用一个HandlerExceptionResolver,只要有一个HandlerExceptionResolver返回了处理结果(不为null),那么后面的HandlerExceptionResolver就被忽略掉了。
- 这种方式提供的HandlerExceptionResolver需要处理比较多事情,如何通过异常匹配到具体的异常处理方法、如何处理返回值等,而这一块Spring已经提供比较好的实现,所以非特殊情况不推荐自行实现HandlerExceptionResolver。
2) 使用ExceptionHandlerExceptionResolver提供的@ControllerAdvice和@ExceptionHandler机制
这种情形也有两种方式:
- 第一种是直接在Controller里的方法加上@ExceptionHandler注解,这样这个方法就会成为处理异常的方法了。参数是要处理的异常,返回值和普通的Controller方法一样规则。如果只希望这个Controller的请求产生的异常有一种特殊的处理方式,那么推荐用这个方法。
- 第二种是在某个类上先加上@ControllerAdvice注解,这样Spring会把这个类作为Bean加载。这个类里的方法如果加上@ExceptionHandler注解,那么也会成为处理异常的方法。如果是处理全局异常,推荐用这个方法。
3 架构一小步
1) 特殊处理某个Controller的异常时,把处理异常处理方法放Controller里,并加上@ExceptionHandler注解。
2) 处理全局异常时,单独用一个类提供方法处理,类上面标注@ControllerAdvice注解,方法标注@ExceptionHandler注解。
1万+

被折叠的 条评论
为什么被折叠?



