面试题答案
一键面试整体架构层面
- 自定义异常体系设计:
- 定义一个基础的自定义异常类,例如
BaseBusinessException
,作为所有业务相关异常的基类。这个基类可以包含一些通用的属性,如错误码、错误信息等。 - 针对不同的业务模块或功能领域,创建继承自
BaseBusinessException
的具体异常类。例如,对于用户模块,可以有UserNotFoundException extends BaseBusinessException
,订单模块可以有OrderStateException extends BaseBusinessException
等。这样在代码中抛出异常时,能够清晰地表明异常所属的业务范畴。
- 定义一个基础的自定义异常类,例如
- 异常处理框架搭建:
- 在应用的全局层面,设置统一的异常处理机制。可以通过Spring Boot的
@ControllerAdvice
和@ExceptionHandler
注解来实现。@ControllerAdvice
用于定义全局异常处理类,@ExceptionHandler
用于处理特定类型的异常。例如:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BaseBusinessException.class) public ResponseEntity<ErrorResponse> handleBusinessException(BaseBusinessException ex) { ErrorResponse errorResponse = new ErrorResponse(ex.getErrorCode(), ex.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } }
- 这里
ErrorResponse
是自定义的错误响应类,包含错误码和错误信息,通过ResponseEntity
返回给客户端合适的HTTP状态码和错误信息。
- 在应用的全局层面,设置统一的异常处理机制。可以通过Spring Boot的
模块间交互
- 异常传递与封装:
- 当一个模块调用另一个模块的方法时,如果被调用模块发生异常,应优先抛出自定义异常。例如,订单模块调用库存模块的扣减库存方法,库存模块如果库存不足,抛出
StockInsufficientException extends BaseBusinessException
。 - 调用模块捕获到异常后,可以选择直接重新抛出,或者根据业务需求进行异常封装。如果需要添加更多上下文信息,可以创建新的异常类并将原始异常作为异常链的一部分。例如:
try { inventoryService.reduceStock(order.getProductId(), order.getQuantity()); } catch (StockInsufficientException ex) { throw new OrderProcessingException("Failed to process order due to stock issue", ex); }
- 这样既保留了原始异常信息,又添加了调用模块的业务上下文,方便后续排查问题。
- 当一个模块调用另一个模块的方法时,如果被调用模块发生异常,应优先抛出自定义异常。例如,订单模块调用库存模块的扣减库存方法,库存模块如果库存不足,抛出
- 接口设计与异常约定:
- 在模块间的接口定义中,明确说明可能抛出的异常类型。例如,在定义用户服务接口
UserService
时,对于getUserById
方法,如果可能抛出UserNotFoundException
,应在方法的Javadoc注释中清晰地说明,这样调用方在使用接口时能够提前做好异常处理准备。
/** * 根据用户ID获取用户信息 * @param userId 用户ID * @return 用户信息 * @throws UserNotFoundException 如果用户不存在 */ User getUserById(Long userId);
- 在模块间的接口定义中,明确说明可能抛出的异常类型。例如,在定义用户服务接口
异常处理策略
- 业务逻辑异常处理:
- 在业务方法内部,当发生不符合业务规则的情况时,抛出相应的自定义业务异常。例如,在注册用户时,如果用户名已存在,抛出
UsernameExistsException extends BaseBusinessException
。 - 捕获异常后,根据业务需求进行处理。可能是返回友好的错误提示给用户,或者记录详细的异常日志以便后续分析。例如:
try { userService.registerUser(user); } catch (UsernameExistsException ex) { logger.error("User registration failed: {}", ex.getMessage(), ex); return "用户名已存在,请重新选择"; }
- 在业务方法内部,当发生不符合业务规则的情况时,抛出相应的自定义业务异常。例如,在注册用户时,如果用户名已存在,抛出
- 技术异常处理与转换:
- 对于技术层面的异常,如数据库操作异常(
SQLException
)、网络异常(IOException
)等,应将其转换为自定义的业务异常。这样可以避免将底层技术细节暴露给上层业务逻辑和客户端。例如,在数据访问层捕获到SQLException
后,转换为DatabaseOperationException extends BaseBusinessException
:
try { // 数据库操作 jdbcTemplate.update("INSERT INTO users (username, password) VALUES (?,?)", user.getUsername(), user.getPassword()); } catch (SQLException ex) { throw new DatabaseOperationException("Database operation failed", ex); }
- 对于技术层面的异常,如数据库操作异常(
- 异常日志记录:
- 在捕获异常的地方,使用日志框架(如Log4j、SLF4J等)记录详细的异常信息。日志应包含异常类型、错误信息、堆栈跟踪等,以便在系统出现问题时能够快速定位和解决。例如:
try { // 业务操作 } catch (BaseBusinessException ex) { logger.error("Business operation failed: {}", ex.getMessage(), ex); }
- 同时,可以根据异常的严重程度设置不同的日志级别,对于影响系统核心功能的异常使用
ERROR
级别,一般性的业务异常可以使用WARN
级别。