自动配置

image.png

核心类的代码

  1. public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
  2. @Bean
  3. public HandlerExceptionResolver handlerExceptionResolver(
  4. @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
  5. List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
  6. configureHandlerExceptionResolvers(exceptionResolvers);
  7. if (exceptionResolvers.isEmpty()) {
  8. //调用 添加ExceptionHandlerExceptionResolver
  9. addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
  10. }
  11. extendHandlerExceptionResolvers(exceptionResolvers);
  12. HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
  13. composite.setOrder(0);
  14. composite.setExceptionResolvers(exceptionResolvers);
  15. return composite;
  16. }
  17. protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
  18. ContentNegotiationManager mvcContentNegotiationManager) {
  19. //添加第一个
  20. ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
  21. exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
  22. exceptionHandlerResolver.setMessageConverters(getMessageConverters());
  23. exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
  24. exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
  25. if (jackson2Present) {
  26. exceptionHandlerResolver.setResponseBodyAdvice(
  27. Collections.singletonList(new JsonViewResponseBodyAdvice()));
  28. }
  29. if (this.applicationContext != null) {
  30. exceptionHandlerResolver.setApplicationContext(this.applicationContext);
  31. }
  32. exceptionHandlerResolver.afterPropertiesSet();//属性填充
  33. exceptionResolvers.add(exceptionHandlerResolver);
  34. //添加第二个
  35. ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
  36. responseStatusResolver.setMessageSource(this.applicationContext);
  37. exceptionResolvers.add(responseStatusResolver);
  38. //添加第三个
  39. exceptionResolvers.add(new DefaultHandlerExceptionResolver());
  40. }
  41. }
  42. public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
  43. implements ApplicationContextAware, InitializingBean {
  44. @Override
  45. public void afterPropertiesSet() {
  46. // Do this first, it may add ResponseBodyAdvice beans
  47. initExceptionHandlerAdviceCache();//初始化全局异常处理器
  48. if (this.argumentResolvers == null) {
  49. List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
  50. this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);//处理入参处理器
  51. }
  52. if (this.returnValueHandlers == null) {
  53. List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
  54. this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);//处理返回值处理器
  55. }
  56. }
  57. private void initExceptionHandlerAdviceCache() {
  58. if (getApplicationContext() == null) {
  59. return;
  60. }
  61. //核心代码,找到所有我们写的全局异常处理的方法
  62. List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
  63. for (ControllerAdviceBean adviceBean : adviceBeans) {
  64. Class<?> beanType = adviceBean.getBeanType();
  65. if (beanType == null) {
  66. throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
  67. }
  68. ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
  69. if (resolver.hasExceptionMappings()) {
  70. //加到缓存中去,方便查找
  71. this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
  72. }
  73. if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
  74. this.responseBodyAdvice.add(adviceBean);
  75. }
  76. }
  77. ...
  78. }
  79. }

我们的代码

  1. @RestControllerAdvice
  2. @Slf4j
  3. public class MyExceptionHandler {
  4. @ExceptionHandler(value = BusException.class)
  5. @ResponseStatus(HttpStatus.FORBIDDEN)
  6. public String bus(BusException ex){
  7. log.error(ex.toString());
  8. return ex.getMessage();
  9. }
  10. }

ServletInvocableHandlerMethod构造器调用链,其中就会去判断HTTP返回码,根据上面的@ResponseStatus(HttpStatus.FORBIDDEN)去取状态码和原因

  1. public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
  2. //1
  3. public ServletInvocableHandlerMethod(Object handler, Method method) {
  4. super(handler, method);
  5. }
  6. //2
  7. public InvocableHandlerMethod(Object bean, Method method) {
  8. super(bean, method);
  9. }
  10. //3
  11. public HandlerMethod(Object bean, Method method) {
  12. Assert.notNull(bean, "Bean is required");
  13. Assert.notNull(method, "Method is required");
  14. this.bean = bean;
  15. this.beanFactory = null;
  16. this.beanType = ClassUtils.getUserClass(bean);
  17. this.method = method;
  18. this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  19. this.parameters = initMethodParameters();
  20. evaluateResponseStatus();
  21. this.description = initDescription(this.beanType, this.method);
  22. }
  23. //4
  24. private void evaluateResponseStatus() {
  25. ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
  26. if (annotation == null) {
  27. annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
  28. }
  29. if (annotation != null) {
  30. this.responseStatus = annotation.code();
  31. this.responseStatusReason = annotation.reason();
  32. }
  33. }
  34. }

我们的Bean

  1. @ResponseStatus(code = HttpStatus.GATEWAY_TIMEOUT,reason = "my gateway")
  2. public class MyResStatus extends RuntimeException {
  3. public MyResStatus() {
  4. }
  5. public MyResStatus(String message) {
  6. super(message);
  7. }
  8. }

controller

  1. //对应我们自定义的运行时异常
  2. @GetMapping("/resstatus11")
  3. @ResponseBody
  4. public MyResStatus resSta11(){
  5. throw new MyResStatus("nono11");
  6. }
  7. //直接用spring的异常
  8. @GetMapping("/resstatus3")
  9. @ResponseBody
  10. public String resSta3() {
  11. throw new ResponseStatusException(HttpStatus.FAILED_DEPENDENCY,"asdasdas");
  12. }

spring如何解析

  1. public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
  2. protected ModelAndView doResolveException(
  3. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  4. try {
  5. //逻辑1 判断异常
  6. if (ex instanceof ResponseStatusException) {
  7. return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
  8. }
  9. //逻辑2 判断异常有这个注解
  10. ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
  11. if (status != null) {
  12. return resolveResponseStatus(status, request, response, handler, ex);
  13. }
  14. //逻辑3 取出套娃里面的异常
  15. if (ex.getCause() instanceof Exception) {
  16. return doResolveException(request, response, handler, (Exception) ex.getCause());
  17. }
  18. }
  19. catch (Exception resolveEx) {
  20. if (logger.isWarnEnabled()) {
  21. logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
  22. }
  23. }
  24. return null;
  25. }
  26. }

我们的代码

  1. @GetMapping("/defaulte")
  2. public String defaulte() throws HttpRequestMethodNotSupportedException {
  3. throw new HttpRequestMethodNotSupportedException("GET");
  4. }

解析代码

  1. public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
  2. //逻辑就是只处理一些特定的异常
  3. //这个是兜底处理的,最后轮到他,最终会调用response.sendError 并且返回空ModelAndView
  4. @Override
  5. @Nullable
  6. protected ModelAndView doResolveException(
  7. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  8. try {
  9. if (ex instanceof HttpRequestMethodNotSupportedException) {
  10. return handleHttpRequestMethodNotSupported(
  11. (HttpRequestMethodNotSupportedException) ex, request, response, handler);
  12. }
  13. else if (ex instanceof HttpMediaTypeNotSupportedException) {
  14. return handleHttpMediaTypeNotSupported(
  15. (HttpMediaTypeNotSupportedException) ex, request, response, handler);
  16. }
  17. else if (ex instanceof HttpMediaTypeNotAcceptableException) {
  18. return handleHttpMediaTypeNotAcceptable(
  19. (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
  20. }
  21. else if (ex instanceof MissingPathVariableException) {
  22. return handleMissingPathVariable(
  23. (MissingPathVariableException) ex, request, response, handler);
  24. }
  25. else if (ex instanceof MissingServletRequestParameterException) {
  26. return handleMissingServletRequestParameter(
  27. (MissingServletRequestParameterException) ex, request, response, handler);
  28. }
  29. else if (ex instanceof ServletRequestBindingException) {
  30. return handleServletRequestBindingException(
  31. (ServletRequestBindingException) ex, request, response, handler);
  32. }
  33. else if (ex instanceof ConversionNotSupportedException) {
  34. return handleConversionNotSupported(
  35. (ConversionNotSupportedException) ex, request, response, handler);
  36. }
  37. else if (ex instanceof TypeMismatchException) {
  38. return handleTypeMismatch(
  39. (TypeMismatchException) ex, request, response, handler);
  40. }
  41. else if (ex instanceof HttpMessageNotReadableException) {
  42. return handleHttpMessageNotReadable(
  43. (HttpMessageNotReadableException) ex, request, response, handler);
  44. }
  45. else if (ex instanceof HttpMessageNotWritableException) {
  46. return handleHttpMessageNotWritable(
  47. (HttpMessageNotWritableException) ex, request, response, handler);
  48. }
  49. else if (ex instanceof MethodArgumentNotValidException) {
  50. return handleMethodArgumentNotValidException(
  51. (MethodArgumentNotValidException) ex, request, response, handler);
  52. }
  53. else if (ex instanceof MissingServletRequestPartException) {
  54. return handleMissingServletRequestPartException(
  55. (MissingServletRequestPartException) ex, request, response, handler);
  56. }
  57. else if (ex instanceof BindException) {
  58. return handleBindException((BindException) ex, request, response, handler);
  59. }
  60. else if (ex instanceof NoHandlerFoundException) {
  61. return handleNoHandlerFoundException(
  62. (NoHandlerFoundException) ex, request, response, handler);
  63. }
  64. else if (ex instanceof AsyncRequestTimeoutException) {
  65. return handleAsyncRequestTimeoutException(
  66. (AsyncRequestTimeoutException) ex, request, response, handler);
  67. }
  68. }
  69. catch (Exception handlerEx) {
  70. if (logger.isWarnEnabled()) {
  71. logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
  72. }
  73. }
  74. return null;
  75. }
  76. //随便找一个举例
  77. protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
  78. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
  79. String[] supportedMethods = ex.getSupportedMethods();
  80. if (supportedMethods != null) {
  81. response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
  82. }
  83. response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
  84. return new ModelAndView();
  85. }
  86. }

主要的类图

image.png
如果能处理,就返回视图,如果不能处理,就抛出异常
实现了Ordered接口,说明有顺序
2.1 2.2 2.3的添加位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers
1是自动配置注册的(ErrorMVCAutoConfiguration里面)
1作用,添加attribute,永远不返回视图,返回null
2.1结合注解使用
2.2配合异常使用
2.3未知
2.4可以自定义处理流程并且可以指定执行顺序

整体流程

tomcat.png

扩展点

自定义HandlerExceptionResolver并制定Order
自定义ErrorViewResolver

最佳实践

常用的就是
@RestControllerAdvice+@ExceptionHandler(value = BusException.class)+@ResponseStatus(HttpStatus.FORBIDDEN)
前后端分离json传递并且是restful风格http状态码