SpringBoot错误处理—-源码解析

1、默认机制

SpringBoot错误处理自动配置都在ErrorMvcAutoConfiguration中,两大核心机制

2、使用@ExceptionHandler标识一个方法,处理用@Controller标注该类发生的指定错误

默认只能处理这个类的该指定错误)

// @ExceptionHandler源码
@Target({ElementType.METHOD})     // 指定了该注解仅能用于方法
@Retention(RetentionPolicy.RUNTIME)   //指定注解会在运行时保留,这允许通过反射运行获取注解访问
@Documented      //注解应该包含生成的 JavaDoc 文档
@Reflective({ExceptionHandlerReflectiveProcessor.class}) 
public @interface ExceptionHandler {
    Class<? extends Throwable&gt;[] value() default {};       
    //它定义了一个名为 value属性,其类型是一个 Class 数组这个数组元素必须是 Throwable 类或其子类通过使用这个注解时,可以value 属性提供一个 Throwable 类型数组
}

1).局部错误处理部分源码

@Controller         //适配服务端渲染    前后分离模式开始
public class WelcomeController {
    //来到首页
    @GetMapping("/")
    public String index(){
        int i=10/0;    //制造错误
        return "index";
    }
    
    @ResponseBody     
    //返回字符串应该直接作为 HTTP 响应体的内容,而不是作为视图名称解析。通常用于返回 JSON 或纯文本等非HTML内容
    @ExceptionHandler(Exception.class)   //传递value数组中
    public String handleException(Exception e){
        return "Ohho~~~,原因:"+e.getMessage();
    }

}

2).测试

3、 创建一个全局错误处理集中处理错误,使用==@ControllerAdvice==注解标注

这个类是集中处理所有@Controller 发生的错误)

//@ControllerAdvice源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component       //标记类为一个 Spring 组件
public @interface ControllerAdvice {
    @AliasFor(
        annotation = Component.class,
        attribute = "value"
    )
    String name() default "";

    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] assignableTypes() default {};

    Class<? extends Annotation>[] annotations() default {};
}

1).全局错误处理部分源码

@ControllerAdvice        //这个类是集中处理所有@Controller发生的错误
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e){
        return "Ohho~~~统一处理所有错误,原因:"+e.getMessage();
    }
}

###2).再创建一个类,使用@Controller注解标注

@Controller
public class HelloController {
    @GetMapping("/haha")
    public String haha(){
        int i = 10/0;        //制造错误
        return "index";
    }
}

3).测试

4、SpringMVC错误处理未能处理(上述处理不存在

第一阶段的处理未解决,错误转发到/error执行后续处理(图中第一阶段失效,第二阶段处理),以下测试过程中,注释掉上文中的全局局部处理代码

1、SpringBoot中自动配置的错误处理机制基于ErrorMvcAutoConfiguration自动配置实现

ErrorMvcAutoConfiguration自动装配类中,SpringBoot在底层写好一个 ==BasicErrorController==的组件,专门处理/error这个请求部分源码如下

@AutoConfiguration(before = WebMvcAutoConfiguration.class) //指定了该自动配置类在 WebMvcAutoConfiguration 之前进行配置
@ConditionalOnWebApplication(type = Type.SERVLET)    //只有在当前应用是一个 Servlet Web 应用时,这个自动配置才会生效
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })   //只有当类路径存在 Servlet 和 DispatcherServlet 时,这个自动配置才会生效
@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class }) //启用指定类的配置属性绑定
public class ErrorMvcAutoConfiguration {

	private final ServerProperties serverProperties; //注入了 ServerProperties 实例。这样的构造方法注入是为了获取应用程序服务器配置

	public ErrorMvcAutoConfiguration(ServerProperties serverProperties) {
		this.serverProperties = serverProperties;  //注入 ServerProperties 实例
	}

    //容器中存在 ErrorAttributes 类型的 Bean 时,才会创建注册当前方法返回的 Bean
	@Bean
	@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
	public DefaultErrorAttributes errorAttributes() {
		return new DefaultErrorAttributes();
	}

    //在容器中存在 ErrorController 类型的 Bean 时,才会创建注册当前方法返回的 Bean
	@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().toList());
	}
    ...
}
2、 BasicErrorController组件

SpringBoot中默认server.error.path=/error,即该类就是处理/error请求的,根据不同类型的请求,如果产生 HTML 内容的请求,匹配public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) 这个方法;否则其他请求类型匹配public ResponseEntity<Map<String, Object>> error(HttpServletRequest request)方法。分别进行处理。(只会匹配其中一个,Spring MVC会尝试匹配与请求路径匹配的RequestMapping

  • 1.部分源码:
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    	private final ErrorProperties errorProperties;

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 */
	public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
		this(errorAttributes, errorProperties, Collections.emptyList());
	}

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 * @param errorViewResolvers error view resolvers
	 */
	public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties,
			List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, errorViewResolvers);
		Assert.notNull(errorProperties, "ErrorProperties must not be null");
		this.errorProperties = errorProperties;
	}

	@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)   //请求类型为HTML 文本
	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);
	}

	@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);
	}
    ...
}
规则如下

5、自定义错误响应

​ 1) 自定义json响应:使用文章第2和第3部分介绍的,使用注解进行统一异常处理

​ 2)自定义页面响应:根据第4部分介绍规则,在对应项目路径"classpath:/METAINF/resources/","classpath:/resources/","classpath:/static/", "classpath:/public/"或者模板引擎目录下,定义错误页面即可

6、测试

1、在项目的resources/templates/目录创建一个error文件夹放入4xx.html、5xx.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   4xx.html
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
5xx.html
</body>
</html>
2、在项目的resources/templates/error文件夹中,再放入404.html、500.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
404.html
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
500.html
</body>
</html>
3、浏览器先后测试上述情况

原文地址:https://blog.csdn.net/qq_45929019/article/details/134764187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_37504.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注