Zademy

Spring Boot 中的 Result 模式:优雅的错误处理

spring-boot; result-pattern
1328 字

Result 模式是 Spring Boot 中传统异常处理的优雅替代方案。受 Rust 等函数式语言启发,它允许显式表示可能失败的操作,提高代码的可读性和健壮性。

为什么使用 Result 模式?

传统异常的问题

// ❌ 传统异常处理
public User findUserById(Long id) throws UserNotFoundException {
    User user = userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("用户未找到: " + id));
    
    if (!user.isActive()) {
        throw new UserInactiveException("用户未激活: " + id);
    }
    return user;
}

问题:

  • 受检异常污染方法签名
  • 错误流不明确
  • 难以组合操作

Result 模式的优势

// ✅ 使用 Result 模式
public Result<User> findUserById(Long id) {
    return userRepository.findById(id)
        .map(Result::success)
        .orElse(Result.failure(new UserNotFoundError("用户未找到: " + id)))
        .flatMap(user -> user.isActive()
            ? Result.success(user)
            : Result.failure(new UserInactiveError("用户未激活: " + id)));
}

基础实现

主 Result 接口

@FunctionalInterface
public interface Result<T> {
    boolean isSuccess();
    boolean isFailure();
    
    T get() throws NoSuchElementException;
    Error getError() throws NoSuchElementException;
    
    // 函数式转换方法
    <U> Result<U> map(Function<T, U> mapper);
    <U> Result<U> flatMap(Function<T, Result<U>> mapper);
    
    // 错误处理
    Result<T> peek(Consumer<T> successConsumer);
    Result<T> peekError(Consumer<Error> errorConsumer);
    Result<T> recover(Function<Error, T> recoveryFunction);
    
    // 转换为 Java 类型
    Optional<T> toOptional();
    Stream<T> toStream();
    
    // 工厂方法
    static <T> Result<T> success(T value) {
        return new Success<>(value);
    }
    
    static <T> Result<T> failure(Error error) {
        return new Failure<>(error);
    }
    
    static <T> Result<T> ofCallable(Callable<T> callable) {
        try {
            return success(callable.call());
        } catch (Exception e) {
            return failure(new SystemError(e));
        }
    }
}

具体实现

public final class Success<T> implements Result<T> {
    private final T value;
    
    public Success(T value) {
        this.value = Objects.requireNonNull(value);
    }
    
    @Override
    public boolean isSuccess() { return true; }
    @Override
    public boolean isFailure() { return false; }
    @Override
    public T get() { return value; }
    @Override
    public Error getError() {
        throw new NoSuchElementException("Success 不包含错误");
    }
    
    @Override
    public <U> Result<U> map(Function<T, U> mapper) {
        try {
            return success(mapper.apply(value));
        } catch (Exception e) {
            return failure(new SystemError(e));
        }
    }
    
    @Override
    public <U> Result<U> flatMap(Function<T, Result<U>> mapper) {
        try {
            return mapper.apply(value);
        } catch (Exception e) {
            return failure(new SystemError(e));
        }
    }
    
    @Override
    public Result<T> peek(Consumer<T> successConsumer) {
        successConsumer.accept(value);
        return this;
    }
    
    @Override
    public Result<T> peekError(Consumer<Error> errorConsumer) {
        return this; // Success 中不执行任何操作
    }
    
    @Override
    public Optional<T> toOptional() {
        return Optional.of(value);
    }
    
    @Override
    public Stream<T> toStream() {
        return Stream.of(value);
    }
}

public final class Failure<T> implements Result<T> {
    private final Error error;
    
    public Failure(Error error) {
        this.error = Objects.requireNonNull(error);
    }
    
    @Override
    public boolean isSuccess() { return false; }
    @Override
    public boolean isFailure() { return true; }
    @Override
    public T get() {
        throw new NoSuchElementException("Failure 不包含值");
    }
    @Override
    public Error getError() { return error; }
    
    @Override
    public <U> Result<U> map(Function<T, U> mapper) {
        return failure(error); // 传播错误
    }
    
    @Override
    public <U> Result<U> flatMap(Function<T, Result<U>> mapper) {
        return failure(error); // 传播错误
    }
    
    @Override
    public Result<T> peek(Consumer<T> successConsumer) {
        return this; // Failure 中不执行任何操作
    }
    
    @Override
    public Result<T> peekError(Consumer<Error> errorConsumer) {
        errorConsumer.accept(error);
        return this;
    }
    
    @Override
    public Optional<T> toOptional() {
        return Optional.empty();
    }
    
    @Override
    public Stream<T> toStream() {
        return Stream.empty();
    }
}

错误系统

基础错误类

public abstract class Error {
    private final String code;
    private final String message;
    private final Throwable cause;
    private final LocalDateTime timestamp;
    
    protected Error(String code, String message, Throwable cause) {
        this.code = code;
        this.message = message;
        this.cause = cause;
        this.timestamp = LocalDateTime.now();
    }
    
    public String getCode() { return code; }
    public String getMessage() { return message; }
    public Throwable getCause() { return cause; }
    public LocalDateTime getTimestamp() { return timestamp; }
    
    @Override
    public String toString() {
        return String.format("[%s] %s", code, message);
    }
}

域错误

public class ValidationError extends Error {
    private final String field;
    
    public ValidationError(String field, String message) {
        super("VALIDATION_ERROR", message, null);
        this.field = field;
    }
    
    public String getField() { return field; }
}

public class BusinessRuleError extends Error {
    public BusinessRuleError(String rule, String message) {
        super("BUSINESS_RULE_VIOLATION",
              String.format("规则 '%s': %s", rule, message), null);
    }
}

public class SystemError extends Error {
    public SystemError(Throwable cause) {
        super("SYSTEM_ERROR", "内部系统错误", cause);
    }
}

public class UserNotFoundError extends Error {
    public UserNotFoundError(String message) {
        super("USER_NOT_FOUND", message, null);
    }
}

public class UserInactiveError extends Error {
    public UserInactiveError(String message) {
        super("USER_INACTIVE", message, null);
    }
}

Spring Boot 集成

使用 Result 的服务层

@Service
@RequiredArgsConstructor
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailValidator emailValidator;
    private final PasswordEncoder passwordEncoder;
    
    public Result<User> createUser(CreateUserRequest request) {
        return validateCreateRequest(request)
            .flatMap(this::checkEmailExists)
            .flatMap(this::encodePassword)
            .flatMap(this::saveUser);
    }
    
    private Result<CreateUserRequest> validateCreateRequest(CreateUserRequest request) {
        List<ValidationError> errors = new ArrayList<>();
        
        if (StringUtils.isBlank(request.getEmail())) {
            errors.add(new ValidationError("email", "邮箱是必需的"));
        } else if (!emailValidator.isValid(request.getEmail())) {
            errors.add(new ValidationError("email", "邮箱无效"));
        }
        
        if (StringUtils.isBlank(request.getPassword())) {
            errors.add(new ValidationError("password", "密码是必需的"));
        } else if (request.getPassword().length() < 8) {
            errors.add(new ValidationError("password", "密码必须至少8个字符"));
        }
        
        return errors.isEmpty()
            ? Result.success(request)
            : Result.failure(new ValidationError(errors.get(0).getField(),
                errors.stream().map(Error::getMessage).collect(Collectors.joining(", "))));
    }
    
    private Result<CreateUserRequest> checkEmailExists(CreateUserRequest request) {
        return userRepository.existsByEmail(request.getEmail())
            ? Result.failure(new BusinessRuleError("EMAIL_UNIQUE",
                "邮箱已被注册"))
            : Result.success(request);
    }
    
    private Result<UserData> encodePassword(CreateUserRequest request) {
        return Result.ofCallable(() -> {
            String encodedPassword = passwordEncoder.encode(request.getPassword());
            return UserData.builder()
                .email(request.getEmail())
                .password(encodedPassword)
                .name(request.getName())
                .active(true)
                .build();
        });
    }
    
    private Result<User> saveUser(UserData userData) {
        return Result.ofCallable(() -> {
            User saved = userRepository.save(userData);
            return saved;
        });
    }
}

使用 Result 的控制器

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    
    private final UserService userService;
    
    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody @Valid CreateUserRequest request) {
        return userService.createUser(request)
            .map(user -> ResponseEntity.status(HttpStatus.CREATED)
                .body(UserResponse.from(user)))
            .recover(this::handleBusinessError)
            .recover(this::handleValidationError)
            .recover(this::handleSystemError)
            .get();
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<?> getUser(@PathVariable Long id) {
        return userService.findUserById(id)
            .map(user -> ResponseEntity.ok(UserResponse.from(user)))
            .recover(this::handleNotFoundError)
            .recover(this::handleSystemError)
            .get();
    }
    
    private ResponseEntity<ErrorResponse> handleBusinessError(Error error) {
        if (error instanceof BusinessRuleError) {
            return ResponseEntity.badRequest()
                .body(ErrorResponse.from(error));
        }
        throw new IllegalStateException("未处理的错误: " + error);
    }
    
    private ResponseEntity<ErrorResponse> handleValidationError(Error error) {
        if (error instanceof ValidationError) {
            return ResponseEntity.badRequest()
                .body(ErrorResponse.from(error));
        }
        throw new IllegalStateException("未处理的错误: " + error);
    }
    
    private ResponseEntity<ErrorResponse> handleNotFoundError(Error error) {
        if (error instanceof UserNotFoundError) {
            return ResponseEntity.notFound().build();
        }
        throw new IllegalStateException("未处理的错误: " + error);
    }
    
    private ResponseEntity<ErrorResponse> handleSystemError(Error error) {
        if (error instanceof SystemError) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ErrorResponse.from(error));
        }
        throw new IllegalStateException("未处理的错误: " + error);
    }
}

支持类

API 错误响应

@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResponse {
    private String code;
    private String message;
    private LocalDateTime timestamp;
    private String path;
    
    public static ErrorResponse from(Error error) {
        return ErrorResponse.builder()
            .code(error.getCode())
            .message(error.getMessage())
            .timestamp(error.getTimestamp())
            .build();
    }
}

域类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserRequest {
    private String email;
    private String password;
    private String name;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserResponse {
    private Long id;
    private String email;
    private String name;
    private boolean active;
    
    public static UserResponse from(User user) {
        return UserResponse.builder()
            .id(user.getId())
            .email(user.getEmail())
            .name(user.getName())
            .active(user.isActive())
            .build();
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserData {
    private String email;
    private String password;
    private String name;
    private boolean active;
}

模式优势

  1. 清晰性:错误流在代码中明确可见
  2. 组合性:便于操作组合
  3. 不变性:Result 对象是不可变的
  4. 函数式:与函数式编程良好集成
  5. 可测试性:使单元测试更容易

结论

Result 模式为 Spring Boot 应用中的错误处理提供了更优雅和健壮的方式,消除了对受检异常的需求,使代码更易读和维护。