本文目录导读:

在Java中处理接口异常,通常涉及接口定义、具体实现和调用方处理三个层面的配合,以下是几种核心的实践方案:
接口层:声明受检异常
在接口方法上使用 throws 声明可能抛出的异常,强制实现类处理或继续抛出。
public interface UserService {
User findUserById(Long id) throws UserNotFoundException, DataAccessException;
void createUser(User user) throws ValidationException, DuplicateUserException;
}
实现类:要么处理异常,要么继续抛出(与接口声明一致或更具体)。
public class UserServiceImpl implements UserService {
@Override
public User findUserById(Long id) throws UserNotFoundException, DataAccessException {
// 如果未找到,抛出UserNotFoundException
// 如果数据库连接失败,抛出DataAccessException
}
}
调用方:必须使用 try-catch 处理或继续抛出。
try {
User user = userService.findUserById(123L);
} catch (UserNotFoundException e) {
log.error("用户不存在: {}", e.getMessage());
// 返回友好的错误信息
} catch (DataAccessException e) {
log.error("系统异常: {}", e.getMessage());
throw new RuntimeException("系统繁忙,请稍后重试");
}
自定义业务异常
创建业务异常类,区分不同类型的错误场景。
// 基础业务异常
public class BusinessException extends RuntimeException {
private final Integer code;
private final String message;
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
// getters
}
// 具体异常子类
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(Long userId) {
super(1001, "用户ID " + userId + " 不存在");
}
}
public class DuplicateUserException extends BusinessException {
public DuplicateUserException(String username) {
super(1002, "用户名 '" + username + "' 已被占用");
}
}
统一异常处理(最常用)
使用 Spring Boot 的 @ControllerAdvice 或 @RestControllerAdvice 全局处理异常,避免在每个接口中重复 try-catch。
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e) {
log.warn("业务异常: code={}, message={}", e.getCode(), e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(e.getCode(), e.getMessage()));
}
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Void>> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, message));
}
// 处理运行时异常(兜底)
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleException(Exception e) {
log.error("系统异常: ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error(500, "系统繁忙,请稍后重试"));
}
// 统一响应封装
@Data
@AllArgsConstructor
public static class ApiResponse<T> {
private Integer code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> error(Integer code, String message) {
return new ApiResponse<>(code, message, null);
}
}
}
使用示例:接口实现中直接抛异常,无需 try-catch。
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUser(@PathVariable Long id) {
User user = userService.findUserById(id); // 异常由全局处理器接管
return ResponseEntity.ok(ApiResponse.success(user));
}
}
处理远程/第三方接口异常
调用外部 API 时的常见处理模式:
public class ExternalApiClient {
private static final int MAX_RETRY = 3;
private static final long RETRY_DELAY_MS = 1000;
public String callExternalService(String param) {
Exception lastException = null;
for (int i = 0; i < MAX_RETRY; i++) {
try {
// 模拟HTTP调用
HttpPost request = new HttpPost("https://external-api.com/data");
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
return EntityUtils.toString(response.getEntity());
} else if (statusCode >= 500) {
// 服务器错误,尝试重试
Thread.sleep(RETRY_DELAY_MS * (i + 1)); // 指数退避
continue;
} else {
// 客户端错误(4xx),不重试
throw new ExternalApiException("接口请求失败,状态码: " + statusCode, statusCode);
}
} catch (IOException | InterruptedException e) {
lastException = e;
log.warn("第{}次调用失败: {}", i + 1, e.getMessage());
}
}
// 所有重试失败
throw new ExternalApiException("外部接口调用失败,已重试" + MAX_RETRY + "次",
503, lastException);
}
}
使用 Optional 代替异常(可选)
对于“可能不存在”的情况,使用 Optional 可以避免抛出异常。
public interface UserService {
Optional<User> findUserById(Long id);
}
// 调用方
User user = userService.findUserById(123L)
.orElseThrow(() -> new UserNotFoundException(123L));
| 场景 | 推荐方案 |
|---|---|
| 控制器层 | 使用 @RestControllerAdvice 统一处理,避免每个接口写 try-catch |
| 服务层 | 抛出自定义 RuntimeException(如 BusinessException) |
| 第三方接口 | 使用重试机制 + 熔断(如 Resilience4j) |
| 参数校验 | 使用 Bean Validation(@Valid)配合全局异常处理 |
| 非必要异常 | 使用 Optional 或返回空集合,避免用异常控制流程 |
核心原则:
- 可控异常:用自定义业务异常 + 全局处理器
- 不可控异常(网络、IO):记录日志、重试、降级、返回友好提示
- 不要吞异常:
catch(Exception e) {}是最坏的做法
这样既能保证接口的健壮性,又能给调用方返回一致的错误信息结构。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播