本文目录导读:

我来详细介绍Java异常全局处理的几种主流实现方案。
Spring Boot 全局异常处理(推荐)
1 创建统一响应类
// 响应状态码枚举
public enum ResultCode {
SUCCESS(200, "成功"),
ERROR(500, "系统异常"),
BAD_REQUEST(400, "请求参数错误"),
UNAUTHORIZED(401, "未授权"),
FORBIDDEN(403, "权限不足"),
NOT_FOUND(404, "资源不存在");
private final int code;
private final String message;
// 构造方法、getter方法省略
}
// 统一响应类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private int code;
private String message;
private T data;
private long timestamp;
public static <T> Result<T> success(T data) {
return new Result<>(ResultCode.SUCCESS.getCode(),
ResultCode.SUCCESS.getMessage(),
data, System.currentTimeMillis());
}
public static <T> Result<T> error(ResultCode resultCode) {
return new Result<>(resultCode.getCode(),
resultCode.getMessage(),
null, System.currentTimeMillis());
}
public static <T> Result<T> error(int code, String message) {
return new Result<>(code, message, null, System.currentTimeMillis());
}
public static <T> Result<T> error(String message) {
return new Result<>(ResultCode.ERROR.getCode(),
message, null, System.currentTimeMillis());
}
}
2 自定义业务异常
// 业务异常类
@Getter
public class BusinessException extends RuntimeException {
private int code;
private String message;
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
public BusinessException(String message) {
super(message);
this.code = ResultCode.ERROR.getCode();
this.message = message;
}
}
// 参数校验异常
public class ParamValidateException extends RuntimeException {
private final String field;
private final String message;
public ParamValidateException(String field, String message) {
super(message);
this.field = field;
this.message = message;
}
// getter方法省略
}
3 全局异常处理器
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.OK)
public Result<?> handleBusinessException(BusinessException e) {
log.error("业务异常:code={}, message={}", e.getCode(), e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理参数校验异常
*/
@ExceptionHandler(ParamValidateException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<?> handleParamValidateException(ParamValidateException e) {
log.error("参数校验异常:field={}, message={}", e.getField(), e.getMessage());
return Result.error(ResultCode.BAD_REQUEST.getCode(), e.getMessage());
}
/**
* 处理参数绑定异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<?> handleValidationException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
Map<String, String> errors = new HashMap<>();
bindingResult.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
log.error("参数校验失败:{}", errors);
return Result.error(ResultCode.BAD_REQUEST.getCode(),
"参数校验失败:" + errors.toString());
}
/**
* 处理HTTP请求方法不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public Result<?> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e) {
log.error("请求方法不支持:{}", e.getMessage());
return Result.error(405, "请求方法不支持:" + e.getMessage());
}
/**
* 处理资源找不到
*/
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Result<?> handleNoHandlerFound(NoHandlerFoundException e) {
log.error("资源不存在:{}", e.getMessage());
return Result.error(ResultCode.NOT_FOUND.getCode(), "请求的资源不存在");
}
/**
* 处理参数类型不匹配
*/
@ExceptionHandler(TypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<?> handleTypeMismatch(TypeMismatchException e) {
log.error("参数类型不匹配:{}", e.getMessage());
return Result.error(ResultCode.BAD_REQUEST.getCode(),
"参数类型不匹配:" + e.getPropertyName());
}
/**
* 处理缺失请求参数
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<?> handleMissingParam(MissingServletRequestParameterException e) {
log.error("缺少必要参数:{}", e.getParameterName());
return Result.error(ResultCode.BAD_REQUEST.getCode(),
"缺少必要参数:" + e.getParameterName());
}
/**
* 处理未知异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<?> handleException(Exception e, HttpServletRequest request) {
log.error("系统异常:uri={}, message={}", request.getRequestURI(), e.getMessage(), e);
return Result.error(ResultCode.ERROR.getCode(), "系统繁忙,请稍后重试");
}
}
使用示例
1 控制器中使用
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@PostMapping("/register")
public Result<UserVO> register(@Valid @RequestBody RegisterDTO registerDTO) {
// 业务逻辑
if (userService.existsByUsername(registerDTO.getUsername())) {
throw new BusinessException(400, "用户名已存在");
}
UserVO userVO = userService.register(registerDTO);
return Result.success(userVO);
}
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
if (id == null || id <= 0) {
throw new ParamValidateException("id", "用户ID无效");
}
UserVO userVO = userService.getUserById(id);
if (userVO == null) {
throw new BusinessException(ResultCode.NOT_FOUND);
}
return Result.success(userVO);
}
}
// 请求DTO
@Data
public class RegisterDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度要在3-50之间")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度要在6-20之间")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
}
Java原生Web应用(非Spring)
1 Filter方式
@WebFilter("/*")
public class ExceptionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (BusinessException e) {
handleException(response, e.getCode(), e.getMessage());
} catch (Exception e) {
log.error("系统异常", e);
handleException(response, 500, "系统内部错误");
}
}
private void handleException(ServletResponse response, int code, String message)
throws IOException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(Result.error(code, message)));
writer.flush();
writer.close();
}
}
高级配置
1 配置文件
# application.yml
spring:
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
# 自定义异常配置
exception:
include-stacktrace: never
log-level: ERROR
2 配置类
@Configuration
public class ExceptionConfig {
@Bean
public FilterRegistrationBean<ExceptionFilter> exceptionFilterRegistration() {
FilterRegistrationBean<ExceptionFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new ExceptionFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}
测试类
@SpringBootTest
@AutoConfigureMockMvc
public class GlobalExceptionHandlerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testBusinessException() throws Exception {
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(404))
.andExpect(jsonPath("$.message").value("资源不存在"));
}
@Test
public void testValidationException() throws Exception {
String invalidUser = "{\"username\":\"\",\"password\":\"123\"}";
mockMvc.perform(post("/api/users/register")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidUser))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(400));
}
}
最佳实践建议
- 区分异常类型:业务异常、系统异常、参数异常分别处理
- 日志记录:不同级别异常记录不同日志
- 安全考虑:生产环境隐藏详细错误信息
- 国际化:支持多语言错误消息
- 监控告警:关键异常告警通知
这样的全局异常处理方案可以统一错误响应格式,提高代码的可维护性和用户体验。