
本文介绍如何通过 Jackson 注解精准控制 Spring Boot 全局异常响应的 JSON 序列化行为,避免 RuntimeException 等父类默认字段(如 stackTrace、cause)被意外暴露,确保仅输出业务定义的字段(如 resultCode、message)。
本文介绍如何通过 jackson 注解精准控制 spring boot 全局异常响应的 json 序列化行为,避免 `runtimeexception` 等父类默认字段(如 `stacktrace`、`cause`)被意外暴露,确保仅输出业务定义的字段(如 `resultcode`、`message`)。
在 Spring Boot 中构建统一异常处理体系时,一个常见且关键的需求是:让自定义业务异常(如 ResourceNotFoundException)在 HTTP 响应中只输出开发者显式定义的字段,彻底屏蔽 RuntimeException 及其父类(如 Throwable)自带的敏感或冗余字段(如 stackTrace、cause、suppressed、localizedMessage)。默认情况下,Spring MVC 使用 Jackson 将异常对象直接序列化为 JSON,而 RuntimeException 继承自 Throwable,其所有公共字段(包括堆栈信息)都会被自动包含,这不仅泄露内部实现细节,还可能带来安全与性能风险。
最简洁、可靠且无需修改全局 Jackson 配置的解决方案,是在自定义异常基类上使用 @JsonIgnoreProperties 注解,显式排除父类继承来的不必要字段。以问题中的 ApplicationException 为例:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@JsonIgnoreProperties({
"stackTrace", // 排除 Throwable 的堆栈数组
"cause", // 排除嵌套异常引用
"suppressed", // 排除被抑制的异常列表
"localizedMessage", // 排除本地化消息(通常与 message 重复)
"message" // ⚠️ 注意:此处可选——若你希望完全由自定义 message 字段控制,也应排除父类的 message
})
@Getter
@Setter
public class ApplicationException extends RuntimeException {
private int resultCode;
private String message; // 自定义业务消息字段
private int httpStatusCode;
private ZonedDateTime throwedAt;
private static final String ZONE_ID = "Europe/Istanbul";
public ApplicationException(int resultCode, String message, HttpStatus httpStatus) {
super(message); // 调用父类构造器,但该 message 不参与序列化
this.resultCode = resultCode;
this.message = message;
this.httpStatusCode = httpStatus.value();
this.throwedAt = ZonedDateTime.now(ZoneId.of(ZONE_ID));
}
}
✅ 关键说明:@JsonIgnoreProperties 作用于类级别,对当前类及其所有父类中匹配的字段名均生效。即使 stackTrace 定义在 Throwable 中,此注解仍能有效屏蔽它。
配合 @ControllerAdvice 全局异常处理器,即可实现干净、可控的响应:
@ControllerAdvice
public class AppExceptionHandler {
@ExceptionHandler(ApplicationException.class)
public ResponseEntity<applicationexception> handleApplicationException(
ApplicationException ex) {
return ResponseEntity
.status(HttpStatus.valueOf(ex.getHttpStatusCode()))
.body(ex);
}
}</applicationexception>
此时,当抛出 ResourceNotFoundException 时,响应将严格符合预期:
{
"resultCode": -1,
"message": "kullanıcısı bulunamadı",
"httpStatusCode": 404,
"throwedAt": "2023-04-12T15:53:34.532371+03:00"
}
其他可行方案对比(供进阶参考)
- @JsonInclude(JsonInclude.Include.NON_DEFAULT):适用于字段有默认值场景,但对 stackTrace(非基本类型)效果有限,且易误伤业务字段。
- @JsonSerialize 自定义序列化器:灵活性最高,但开发成本高,维护复杂,小项目不推荐。
- 全局 ObjectMapper 配置(如 spring.jackson.deserialization.fail-on-unknown-properties=false):无法精准控制特定类,影响范围过大,易引发副作用。
注意事项与最佳实践
- 勿遗漏 message 字段:RuntimeException 自带 message,若子类也定义同名字段,Jackson 默认会序列化父类的 message(即构造器传入的值)。因此建议在 @JsonIgnoreProperties 中一并排除,并确保子类 message 字段被正确使用(如示例中 super(message) 仅用于异常链,实际响应走自定义字段)。
- 保持 @Getter/@Setter 与注解兼容:Lombok 的 @Getter 生成的 getter 方法仍受 @JsonIgnoreProperties 影响,无需额外处理。
- 测试覆盖所有派生类:确保 ResourceNotFoundException、InvalidRequestException 等子类在抛出时均遵循同一序列化规则——因继承关系,@JsonIgnoreProperties 自动生效。
- 生产环境务必禁用堆栈详情:除序列化控制外,还应确保 server.error.include-stacktrace=never(application.yml),从 Web 层彻底关闭堆栈暴露。
通过这一轻量级注解方案,你能在不侵入框架底层的前提下,精准掌控异常响应结构,兼顾安全性、可读性与可维护性。










