Java案例怎么处理接口异常?

wen java案例 76

本文目录导读:

Java案例怎么处理接口异常?

  1. 接口层:声明受检异常
  2. 自定义业务异常
  3. 统一异常处理(最常用)
  4. 处理远程/第三方接口异常
  5. 使用 Optional 代替异常(可选)

在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) {} 是最坏的做法

这样既能保证接口的健壮性,又能给调用方返回一致的错误信息结构。

抱歉,评论功能暂时关闭!