Help us improve
Share bugs, ideas, or general feedback.
From java
Java错误处理规范 - sealed exception层次设计、RFC 9457 Problem Details响应、Optional空值安全、SLF4J结构化日志。设计异常体系、处理错误、编写日志或调试异常堆栈时加载。
npx claudepluginhub lazygophers/ccplugin --plugin javaHow this skill is triggered — by the user, by Claude, or both
Slash command
/java:errorsonnetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- **java:dev** - 错误处理设计和实现
Enforces Java development conventions including naming rules, exception handling, Spring Boot best practices, DTO/VO Lombok usage, batch query limits, and N+1 query prevention.
Provides Java 17+ coding standards for Spring Boot services covering naming, immutability, Optional usage, Streams, exceptions, generics, and project layout.
Provides best practices for modern Java (17+) code: null safety with Optional and annotations, immutability via records, concurrency with CompletableFuture and virtual threads, sealed classes.
Share bugs, ideas, or general feedback.
使用 sealed interface 构建类型安全的异常层次结构,编译器确保 switch exhaustiveness。
// 定义 sealed 业务异常层次
public sealed interface AppException permits
ResourceNotFoundException,
DuplicateResourceException,
ValidationException,
AuthorizationException {
String code();
String message();
}
// 具体异常使用 Record 实现(不可变)
public record ResourceNotFoundException(String resourceType, String resourceId)
implements AppException {
public String code() { return "NOT_FOUND"; }
public String message() { return "%s not found: %s".formatted(resourceType, resourceId); }
}
public record DuplicateResourceException(String field, String value)
implements AppException {
public String code() { return "DUPLICATE"; }
public String message() { return "Duplicate %s: %s".formatted(field, value); }
}
// 作为 RuntimeException 抛出
public class AppRuntimeException extends RuntimeException {
private final AppException appException;
public AppRuntimeException(AppException appException) {
super(appException.message());
this.appException = appException;
}
public AppException appException() { return appException; }
}
// 全局异常处理器 - Problem Details 标准格式
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AppRuntimeException.class)
public ProblemDetail handleAppException(AppRuntimeException ex) {
AppException appEx = ex.appException();
return switch (appEx) {
case ResourceNotFoundException e -> {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, e.message());
pd.setTitle("Resource Not Found");
pd.setProperty("resourceType", e.resourceType());
pd.setProperty("resourceId", e.resourceId());
yield pd;
}
case DuplicateResourceException e -> {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(
HttpStatus.CONFLICT, e.message());
pd.setTitle("Duplicate Resource");
pd.setProperty("field", e.field());
yield pd;
}
case ValidationException e -> ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST, e.message());
case AuthorizationException e -> ProblemDetail.forStatusAndDetail(
HttpStatus.FORBIDDEN, e.message());
};
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST, "Validation failed");
pd.setTitle("Validation Error");
pd.setProperty("errors", ex.getFieldErrors().stream()
.map(e -> Map.of("field", e.getField(), "message", e.getDefaultMessage()))
.toList());
return pd;
}
}
// application.yml 启用 Problem Details
// spring:
// mvc:
// problemdetails:
// enabled: true
// Service 层返回 Optional
@Transactional(readOnly = true)
public Optional<UserResponse> findById(Long id) {
return userRepository.findById(id).map(UserResponse::from);
}
// Controller 层处理 Optional
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElseThrow(() -> new AppRuntimeException(
new ResourceNotFoundException("User", id.toString())));
}
// Optional 链式操作
Optional<String> email = userRepository.findById(id)
.filter(User::isActive)
.map(User::getEmail);
// 禁止模式
// bad: optional.get() 不检查
// bad: optional.isPresent() + optional.get()
// bad: return null
// good: orElseThrow / orElse / map / flatMap
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
public User create(CreateUserRequest request) {
log.info("Creating user: email={}", request.email());
try {
User user = userRepository.save(toEntity(request));
log.info("User created: id={}, email={}", user.getId(), user.getEmail());
return user;
} catch (DataIntegrityViolationException e) {
log.warn("Duplicate email: email={}", request.email());
throw new AppRuntimeException(new DuplicateResourceException("email", request.email()));
} catch (Exception e) {
log.error("Failed to create user: email={}", request.email(), e);
throw e;
}
}
}
// 日志级别规范
// ERROR - 系统错误,需要立即处理(异常、数据不一致)
// WARN - 业务异常,可预期但需关注(重复请求、参数无效)
// INFO - 业务关键节点(创建、更新、删除、登录)
// DEBUG - 开发调试信息(仅开发环境)
// TRACE - 详细跟踪(仅排查特定问题)
// 多资源自动关闭
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
process(rs);
}
}
// 自定义 AutoCloseable
public class DatabaseSession implements AutoCloseable {
@Override
public void close() {
// 释放资源
}
}
| AI 可能的理性化解释 | 实际应该检查的内容 |
|---|---|
| "抛 RuntimeException 通用" | 是否使用 sealed exception 层次结构? |
| "返回 null 更简单" | 是否使用 Optional? |
| "空 catch 先不管" | catch 块是否至少记录日志? |
| "System.out 调试就行" | 是否使用 SLF4J 参数化日志? |
| "返回 HTTP 500 通用错误" | 是否实现 RFC 9457 Problem Details? |
| "optional.get() 直接取" | 是否使用 orElseThrow/map/flatMap? |