15、异常处理


【尚硅谷】SpringBoot2零基础入门教程-讲师:雷丰阳
笔记

路还在继续,梦还在期许

1、错误处理

1.1、默认规则

  • 默认情况下,spring boot 提供 /error 处理所有错误的映射。
  • 对于机器客户端(app),它将生成 JSON 响应,其中包含错误,HTTP状态和异常消息的详细信息
  • 对于浏览器客户端,响应一个 “ whitelabel” 错误视图,以HTML格式呈现相同的数据。

在这里插入图片描述

在这里插入图片描述

1.2、自定义错误页面

  • 要对其进行自定义,添加 View 解析为 error。
  • 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加 ErrorAttributes 类型的组件以使用现有机制但替换其内容。
  • error/下的4xx,5xx页面会被自动解析。
src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

在这里插入图片描述

1.3、定制错误处理逻辑

1.3.1、自定义错误页

error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页。

1.3.2、@ControllerAdvice+@ExceptionHandler处理全局异常

底层是 ExceptionHandlerExceptionResolver 支持的(推按使用这种方式)

// 处理整个web controller的异常
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    
    
    
    @ExceptionHandler({
    
    ArithmeticException.class, NullPointerException.class}) // 异常处理
    public String handlerArithException(Exception e){
    
    
        log.error("异常是:{}",e);
        return "login"; // 视图地址
    }
}

1.3.3、@ResponseStatus+自定义异常

底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息组装成ModelAndView返回。

底层调用 response.sendError(statusCode, resolvedReason);这样就结束了,给tomcat发送的错误 /error。

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "用户数量太多") // 给页面返回一个异常状态码403,和错误的原因
public class UserTooManyException extends RuntimeException {
    
    
    
    public UserTooManyException() {
    
    

    }

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

}

1.3.4、Spring底层的异常

如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。

最后调用 response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());

此次请求结束,给Tomcat服务器发送错误,/error 谁能处理就处理,无人处理就响应Tomcat原生错误页。

在这里插入图片描述

1.3.5、自定义实现 HandlerExceptionResolver 处理异常

可以作为默认的全局异常处理规则

在这里插入图片描述

@Order(value = Ordered.HIGHEST_PRECEDENCE) // 指定优先级
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
    
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Object handler, Exception ex) {
    
    
        try {
    
    
            response.sendError(511, "自定义错误");
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
        ModelAndView modelAndView = new ModelAndView();
        return modelAndView;
    }
}

1.3.6、ErrorViewResolver 实现自定义处理异常

response.sendError ,error请求就会转给controller。

转给controller的两种方式

方式一

调用 response.sendError 转给 controller。

方式二

你的异常没有任何人能处理,tomcat底层 response.sendError,error请求就会转给controller。

解析规则

basicErrorController 要去的页面地址是 ErrorViewResolver ;

1.4、异常处理自动配置原理

位置:org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

ErrorMvcAutoConfiguration 自动配置异常处理规则

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({
    
     Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({
    
     ServerProperties.class, WebMvcProperties.class })
public class ErrorMvcAutoConfiguration {
    
    
}

1.4.1、DefaultErrorAttributes 类

容器中添加组件:类型:DefaultErrorAttributes -> id:errorAttributes

@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
    
    
	return new DefaultErrorAttributes();
}

DefaultErrorAttributes类,实现了 ErrorAttributes 接口和 HandlerExceptionResolver 接口。

public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
    
    }

DefaultErrorAttributes:定义错误页面中可以包含哪些数据。

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
		Exception ex) {
    
    
	storeErrorAttributes(request, ex);
	return null;
}

@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
    
    
	Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));
	if (Boolean.TRUE.equals(this.includeException)) {
    
    
		options = options.including(Include.EXCEPTION);
	}
	if (!options.isIncluded(Include.EXCEPTION)) {
    
    
		errorAttributes.remove("exception");
	}
	if (!options.isIncluded(Include.STACK_TRACE)) {
    
    
		errorAttributes.remove("trace");
	}
	if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
    
    
		errorAttributes.put("message", "");
	}
	if (!options.isIncluded(Include.BINDING_ERRORS)) {
    
    
		errorAttributes.remove("errors");
	}
	return errorAttributes;
}

在这里插入图片描述
在这里插入图片描述

1.4.2、BasicErrorController 类

容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
		ObjectProvider<ErrorViewResolver> errorViewResolvers) {
    
    
	return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
			errorViewResolvers.orderedStream().collect(Collectors.toList()));
}

处理默认 /error 路径的请求;页面响应 new ModelAndView(“error”, model);

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    }

它的两个处理方法:

响应JSON

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    
    
	HttpStatus status = getStatus(request);
	if (status == HttpStatus.NO_CONTENT) {
    
    
		return new ResponseEntity<>(status);
	}
	Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
	return new ResponseEntity<>(body, status);
}

响应HTML

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    
    
	HttpStatus status = getStatus(request);
	Map<String, Object> model = Collections
			.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
	response.setStatus(status.value());
	ModelAndView modelAndView = resolveErrorView(request, response, status, model);
	return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

1.4.3、内部类 ErrorPageCustomizer 类

容器中有组件 View->id是error;(响应默认错误页)

容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
@Conditional(ErrorTemplateMissingCondition.class)
protected static class WhitelabelErrorViewConfiguration {
    
    

	private final StaticView defaultErrorView = new StaticView();

	@Bean(name = "error")
	@ConditionalOnMissingBean(name = "error")
	public View defaultErrorView() {
    
    
		return this.defaultErrorView;
	}

	// If the user adds @EnableWebMvc then the bean name view resolver from
	// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.
	@Bean
	@ConditionalOnMissingBean
	public BeanNameViewResolver beanNameViewResolver() {
    
    
		BeanNameViewResolver resolver = new BeanNameViewResolver();
		resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
		return resolver;
	}

}

如果想要返回页面;就会找error视图【StaticView】。(默认是一个白页)

1.4.4、错误视图解析器

位置:org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver 接口

@FunctionalInterface
public interface ErrorViewResolver {
    
    

	/**
	 * Resolve an error view for the specified details.
	 * @param request the source request
	 * @param status the http status of the error
	 * @param model the suggested model to be used with the view
	 * @return a resolved {@link ModelAndView} or {@code null}
	 */
	ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model);

}

位置:org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver

实现 ErrorViewResolver 接口

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
    
    }

在过时的配置中配置了 DefaultErrorViewResolver 组件

容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver

@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({
    
     org.springframework.boot.autoconfigure.web.ResourceProperties.class,
		WebProperties.class, WebMvcProperties.class })
static class DefaultErrorViewResolverConfiguration {
    
    

	private final ApplicationContext applicationContext;

	private final Resources resources;

	DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,
			org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
			WebProperties webProperties) {
    
    
		this.applicationContext = applicationContext;
		this.resources = webProperties.getResources().hasBeenCustomized() ? webProperties.getResources()
				: resourceProperties;
	}

	@Bean
	@ConditionalOnBean(DispatcherServlet.class)
	@ConditionalOnMissingBean(ErrorViewResolver.class)
	DefaultErrorViewResolver conventionErrorViewResolver() {
    
    
		return new DefaultErrorViewResolver(this.applicationContext, this.resources);
	}

}

如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面。

在 error 路径下找 404.html、5xx.html。

1.5、异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException 封装
2、进入视图解析流程(页面渲染)

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

  • 1、遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】

在这里插入图片描述

  • 2、系统默认的异常解析器;

在这里插入图片描述

○ 1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
○ 2、默认没有任何人能处理异常,所以异常会被抛出
■ 1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
■ 2、解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。

在这里插入图片描述
■ 3、默认的 ErrorViewResolver 作用是把响应状态码作为错误页面地址返回 error/状态码.html
■ 4、模板引擎最终响应这个页面 error/500.html

猜你喜欢

转载自blog.csdn.net/zhao854116434/article/details/130251159