目录
一、前言
二、SpringBoot 异常介绍
2.1 SpringBoot 中异常定义
2.1.1 SpringBoot 异常处理机制的重要性
2.2 常用的异常分类
2.3 常用的异常处理解决方案
三、springboot 异常处理操作实践
3.1 springboot自适应错误处理机制
3.1.1 使用默认错误页面
3.1.2 自定义配置
3.2 springmvc 错误处理方案
3.2.1 局部控制 @ExceptionHandler 的使用
1)默认错误页面案例
2)使用@ExceptionHandler改造
3.2.2 全局控制器
3.3 springboot 错误处理方案
3.3.1 springboot 错误处理流程
3.3.1.1 如何在错误页面获取错误信息
3.3.1.2 自定义错误页面
3.3.1.3 补充说明
3.4 前后端分离项目错误处理方案
3.4.1 非前后端分离的项目错误处理方案
四、写在文末
一、前言
在使用springboot进行微服务的开发时,经常会涉及到关于系统错误和系统异常等情况的处理,在实际业务中,项目一旦上线之后,在使用过程中总会遇到各种意料之外的情况,此时就需要借助框架对异常的处理机制进行代码层面的完善,常见的情况比如说,代码层面错误,查询数据库错误,调用第三方API错误,未被捕获的错误等,本文将详细介绍如何在微服务开发中更合理的使用springboot的异常处理机制。
二、SpringBoot 异常介绍
2.1 SpringBoot 中异常定义
在 Spring Boot 应用程序中,异常(Exception)是指在程序执行过程中发生的、干扰正常流程的事件。Spring Boot 提供了一套强大的机制来管理和处理这些异常,确保应用程序能够在遇到错误时以一种受控的方式进行响应,并向用户或客户端提供有意义的反馈。
-
异常(Exception)是在程序执行过程中出现的一种特殊情况或错误。它可以是由于程序逻辑错误、运行环境问题、用户输入错误等原因导致的一种非正常的状态或事件。
-
在编程领域中,异常通常用来表示一种无法预料或处理的情况,它会导致程序无法继续正常执行。当程序运行过程中遇到异常,如果没有适当的处理机制,可能会导致程序崩溃或产生未预期的结果。
2.1.1 SpringBoot 异常处理机制的重要性
通常来说,在controller层如果程序出现了异常,并且这个异常未被捕获,springboot提供的异常处理机制将生效。Spring Boot 提供异常处理机制主要是为了提高应用的健壮性和用户体验。它的好处包括:
-
统一错误响应:可以定义全局异常处理器来统一处理各种异常,确保返回给客户端的错误信息格式一致,便于前端解析。
-
提升用户体验:能够优雅地处理异常情况,避免直接将技术性错误信息暴露给用户,而是显示更加友好的提示信息。
-
简化代码:开发者不需要在每个可能抛出异常的方法中重复编写异常处理逻辑,减少冗余代码,使业务代码更加清晰简洁。
-
增强安全性:通过控制异常信息的输出,防止敏感信息泄露,增加系统的安全性。
2.2 常用的异常分类
下面是关于 Spring Boot 异常的一些关键概念和分类:
-
已检查异常(Checked Exceptions)
-
定义:这是 Java 编译器强制要求处理的一类异常,通常是由外部因素引起的,如文件读写失败、网络连接问题等。
-
示例:
IOException
,SQLException
等。
-
-
未检查异常(Unchecked Exceptions 或 RuntimeExceptions)
-
定义:也称为运行时异常,通常是由于编程错误导致的,例如空指针异常(NullPointerException)、数组越界异常(ArrayIndexOutOfBoundsException)等。这类异常不需要强制捕获,但处理它们对于保证应用的健壮性非常重要。
-
示例:IllegalArgumentException, IllegalStateException 等。
-
-
业务逻辑异常
-
定义:这些是开发者根据具体业务场景自定义的异常类型,用于表达特定的业务规则违反情况。
-
示例:ResourceNotFoundException, AccessDeniedException 等。
-
-
系统级异常
-
定义:当应用程序遭遇严重错误,比如内存不足、线程死锁等问题时抛出的异常。这类异常通常难以恢复,可能需要重启服务。
-
示例:OutOfMemoryError, StackOverflowError 等。
-
-
HTTP 相关异常
-
定义:与 HTTP 请求和响应相关的异常,通常发生在控制器层,用于映射到特定的 HTTP 状态码。
-
示例:HttpMessageNotReadableException, MethodArgumentNotValidException 等。
-
2.3 常用的异常处理解决方案
在实际开发中,如果遇到异常需要处理,可以根据如下提供的几种方法选择合适的进行处理,不同的处理办法需要结合实际情况酌情选择:
-
捕获异常并处理
-
使用 try-catch 块来捕获可能抛出异常的代码块。
-
在 try 块中编写可能引发异常的代码,然后在 catch 块中处理异常。
-
这种方式适用于已检查异常(checked exception),以及可以预料到可能出现的异常情况。
-
-
抛出异常
-
在方法中使用 throw 关键字抛出异常,告知调用者可能发生的异常情况。
-
通常在方法内部检测到无法处理的情况时抛出异常,将问题交给上层调用者处理。
-
-
使用finally 关键字
-
finally 块中的代码总是会被执行,无论是否抛出异常,用于释放资源或者确保某些操作一定会执行。
-
-
使用 try-with-resources
-
对于实现了 AutoCloseable 接口的资源,可以使用 try-with-resources 语句,确保资源在使用后自动关闭。
-
适用于需要手动关闭的资源管理,如文件操作、数据库连接等。
-
三、springboot 异常处理操作实践
3.1 springboot自适应错误处理机制
Spring Boot 的自适应错误处理机制是指应用程序能够根据不同的环境(如开发、测试或生产)以及客户端的需求,自动调整其错误响应的方式。这种机制不仅提高了用户体验,还简化了开发和运维工作。其主要处理机制和实现方式如下。
3.1.1 使用默认错误页面
Spring Boot 提供了一个内置的错误控制器 (ErrorController) 和一个默认的错误页面,当发生未处理的异常时,会自动展示该页面。这个页面可以根据请求的内容类型协商策略返回不同格式的错误信息。
-
HTML 格式:对于浏览器用户,默认错误页面会以 HTML 形式呈现,包含错误摘要、状态码、时间戳等信息。
-
JSON 或 XML 格式:对于 API 请求,默认错误页面则会返回 JSON 或 XML 格式的错误信息,适合被其他系统解析。
返回的错误格式数据样例如下:
{"timestamp": "2024-12-12T12:34:56.789+00:00","status": 404,"error": "Not Found","message": "No message available","path": "/nonexistent"
}
比如在我们的工程中,并没有updateUser这样的接口,服务启动之后,调用一下这个接口看到下面的错误页面和信息
默认情况下,这里返回的是一个错误的html页面,由springboot框架自身集成处理的,如果在实际开发中,认为这样的错误也需要前端页面做下兼容处理,可以考虑让框架针对这种错误返回json错误信息,这就是在上一篇探讨springboot的内容协商机制时候谈到的,最简单的方式就是在请求头中通过Accept来指定,参考下面的调用方式:
-
只需要指定请求的类型即可,如果是text/html,返回的就是一个错误页面,像上面那个页面就是;
3.1.2 自定义配置
开发者可以通过配置文件(如 application.properties
或 application.yml
)来自定义错误处理行为,参考下面的做法:
-
启用/禁用堆栈跟踪显示:
-
在开发环境中可以显示完整的异常堆栈跟踪,而在生产环境中则隐藏这些敏感信息。
-
-
设置全局错误属性:
-
如错误消息模板、是否包含异常详情等。
-
-
覆盖默认错误页面:
-
通过创建自己的 ErrorController 实现或静态资源文件夹中的 HTML 文件来替代默认的错误页面。
-
参考配置示例:
# application.properties
server.error.include-stacktrace=ALWAYS # 开发模式下总是显示堆栈跟踪
server.error.include-message=ALWAYS # 总是包含错误消息# application.yml
server:error:include-stacktrace: ALWAYSinclude-message: ALWAYS
3.2 springmvc 错误处理方案
针对错误的处理上,springmvc与springboot两个框架上面存在一定的差异,大家都知道,springboot后来居上,作为服务端的开发脚手架,内置了springmvc的所有功能,这就是说springmvc框架自身的一些机制在springboot框架中都能兼容和正常使用,但就具体使用的场景来说,仍然存在一些差一点,这里做额外的说明。
注意点:如果代码中使用了SpringMVC的错误处理方案,SpringBoot的错误处理方案不生效。
3.2.1 局部控制 @ExceptionHandler 的使用
顾名思义,对局部生效,具体到代码层面,即对某个具体的controller控制器生效,而对其他的控制器不生效,具体来说,使用@ExceptionHandler这个注解配合完成,使用方式为:
-
在控制器当中编写一个方法,方法使用@ExceptionHandler注解进行标注,凡是这个控制器当中出现了对应的异常,则走这个方法来进行异常的处理。局部生效。
1)默认错误页面案例
在下面的一个controller接口中,如果满足参数的要求,将会抛出指定类型的异常
@GetMapping("/detail")
public Object userDetail(@RequestParam Integer id){if(id == 1){throw new IllegalArgumentException("无效ID:" + id);}return "ID = " + id;
}
请求测试看下效果,此时抛出了IllegalArgumentException这种类型的异常
-
在当前情况下,还没有使用springmvc的局部处理方案,就走了springboot默认的错误处理方案,显示的是下面这个错误页面;
2)使用@ExceptionHandler改造
只需要在当前的controller中添加下面的错误处理方法
@ExceptionHandler(IllegalArgumentException.class)
public String handler(IllegalArgumentException e){return "错误信息:" + e.getMessage();
}
然后启动服务再次测试一下,此时自定义的局部错误处理的异常返回格式数据就生效了
3.2.2 全局控制器
使用@ControllerAdvice + @ExceptionHandler这两个组合注解可以实现全局异常控制器的作用,具体来说,将上面局部生效的方法单独放到一个类当中,这个类使用@ControllerAdvice注解标注,凡是任何控制器当中出现了对应的异常,则走这个方法来进行异常的处理,即全局生效。如下重新定义一个类,使用这两个注解的组合。
package com.congge.config;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(IllegalArgumentException.class)@ResponseBodypublic String handler(IllegalArgumentException e){return "错误信息:" + e.getMessage();}
}
然后将上一个示例中的局部异常处理方案注释掉,再次运行项目测试,仍然也能生效,说明全局控制器生效了
3.3 springboot 错误处理方案
上面分享的主要是springmvc框架在发生异常时的主要处理方案,如果在你的项目中配置了springmvc相关的异常配置,那么springboot框架自身的异常方案将不会生效,但是在前后端分离模式比较流行的当下,springboot框架自身不仅兼容springmvc的异常处理,还在其基础上做了一定的扩展,从而使得在开发过程中更灵活的进行选择和使用。
为了方便后续操演演示看效果,这里采用thymeleaf这个模板渲染组件,在pom文件中提前导入下面的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.3.1 springboot 错误处理流程
SpringBoot默认的错误处理方案如下:
-
如果客户端要的是json,则直接响应json格式的错误信息。
-
这个在前面的接口测试中已经演示了效果。
-
-
如果客户端要的是html页面,则按照下面的步骤:
-
第一步(精确错误码文件):
-
去classpath:/templates/error/目录下找404.html 、500.html等精确错误码.html文件,如果找不到,则去静态资源目录下的/error目录下找,如果还是找不到,才会进入下一步。
-
-
第二步(模糊错误码文件):
-
去classpath:/templates/error/目录下找4xx.html5xx.html等模糊错误码.html文件。如果找不到,则去静态资源目录下的/error目录下找。如果还是找不到,才会进入下一步。
-
-
第三步(通用错误页面):
-
去找classpath:/templates/error.html如果找不到则进入下一步。
-
-
第四步(默认错误处理):
-
如果上述所有步骤都未能找到合适的错误页面,Spring Boot 会使用内置的默认错误处理机制,即 /error 端点。
-
-
3.3.1.1 如何在错误页面获取错误信息
如果需要通过自定义错误页面的方式获取到系统的错误或堆栈信息怎么做呢?Spring Boot 默认会在模型Model中存放这些详细的错误,具体来说,包括下面一些常用的错误元素:
-
timestamp: 错误发生的时间戳
-
status: HTTP 状态码
-
error: 错误类型(如 "Not Found")
-
exception: 异常类名
-
message: 错误消息
-
trace: 堆栈跟踪
在thymeleaf中使用 ,通过类似这样的标签
${message}
即可取出信息
3.3.1.2 自定义错误页面
如下,在resources目录的templates目录下自定义一个error.html的文件,使用thymeleaf模板渲染的方式将系统的错误和堆栈信息等输出到error.html页面
error.html内容如下:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>error</title>
</head>
<body><h1>通用错误</h1>
异常发生时间:<span th:text="${timestamp}"></span><br>
HTTP状态码:<span th:text="${status}"></span><br>
错误类型:<span th:text="${error}"></span><br>
异常类名:<span th:text="${exception}"></span><br>
错误信息:<span th:text="${message}"></span><br>
堆栈信息:<span th:text="${trace}"></span><br>
</body>
</html>
启动服务,然后请求一个不存在的接口,可以看到能够按照预期目标输出相关的错误信息
使用了自定义错误页面来获取系统的异常信息之后,那么之前的全局异常处理器返回json的格式数据就要做调整,因为需要通过自定义页面来承载各种错误信息,所以将上述的自定义全局异常处理器先去掉,再次请求一下异常的测试接口,此时能看到如下信息,说明自定义错误页面生效了
3.3.1.3 补充说明
注意:
-
springboot3.3.5版本默认只向Model对象中绑定了timestampstatuserror。如果要保存exceptionmessagetrace,需开启以下三个配置:
server.error.include-stacktrace=always
server.error.include-exception=true
server.error.include-message=always
添加上了这些配置之后,再次请求一下不存在的接口,此时堆栈的详细信息就能输出了,这样更便于排查问题
3.4 前后端分离项目错误处理方案
在当下流行前后端分离的项目开发模式下,使用springboot框架自身的错误页面来处理并非不可,而是做出来的效果没有那么好,所以在真实的开发中,建议使用SpringMVC的错误处理方案,具体来说:
-
定义全局异常处理机制,即使用 @ControllerAdvice + @ExceptionHandler这两个注解,配合自定义全局异常处理器的方式;
-
异常处理类中返回json格式的错误信息,其它就不需要管了,因为前端接收到错误信息之后会按照业务的规范统一处理。
3.4.1 非前后端分离的项目错误处理方案
如果在你的项目中仍然存在前后端不分离的情况,建议使用SpringBoot的错误处理方案,参考下面的建议:
-
如果发生的异常是HTTP错误状态码:
-
建议常见的错误码给定
精确错误码.html
-
建议不常见的错误码给定
模糊错误码.html
-
-
如果发生的异常不是HTTP错误状态码,而是业务相关异常:
-
在程序中处理具体的业务异常,自己通过程序来决定跳转到哪个错误页面。
-
-
建议提供
classpath:/templates/error.html
来处理通用错误。
四、写在文末
本文通过较大的篇幅结合案例操作详细分享了springboot项目中针对异常或错误的通用处理方案,异常处理可以说在任何一个微服务项目中都会涉及并需要处理的,具有较强的实践意义,希望对看到的同学有用,本篇到此结束,感谢观看。