Spring Boot生产级异常处理:自定义异常返回指定HTTP状态与消息
Alright, let's walk through exactly how to set this up for your Business/Web/Domain module structure. The goal is to have custom exceptions that trigger user-friendly HTTP responses instead of generic ones like "internal server error". Here's a step-by-step approach:
1. Define Custom Exceptions in the Domain Layer
Since the Domain layer is shared across Business and Web, this is the perfect place to define your exception hierarchy. Start with a base exception class, then create specific exceptions for each scenario you need (like your 500 "请稍后重试" case).
// Domain/src/main/java/com/yourcompany/domain/exceptions/BaseCustomException.java public class BaseCustomException extends RuntimeException { private final int httpStatus; private final String userMessage; public BaseCustomException(int httpStatus, String userMessage) { super(userMessage); this.httpStatus = httpStatus; this.userMessage = userMessage; } // Getters for httpStatus and userMessage public int getHttpStatus() { return httpStatus; } public String getUserMessage() { return userMessage; } } // Domain/src/main/java/com/yourcompany/domain/exceptions/ServiceUnavailableException.java import org.springframework.http.HttpStatus; public class ServiceUnavailableException extends BaseCustomException { public ServiceUnavailableException() { // 500 status code, custom message for UI super(HttpStatus.INTERNAL_SERVER_ERROR.value(), "请稍后重试"); } }
You can add more exceptions here later (e.g., ResourceNotFoundException for 404, BadRequestException for 400) following the same pattern.
2. Implement Global Exception Handling in the Web Layer
The Web layer holds your Controllers, so we'll use a global exception handler here to catch your custom exceptions and format the HTTP response properly. Use @RestControllerAdvice to apply this to all your REST controllers.
First, create a unified response DTO to send back to the UI (this can live in Domain or Web, depending on your setup):
// Web/src/main/java/com/yourcompany/web/dto/ApiResponse.java public class ApiResponse<T> { private int status; private String message; private T data; // Constructor, getters, setters public ApiResponse(int status, String message, T data) { this.status = status; this.message = message; this.data = data; } }
Then the global exception handler:
// Web/src/main/java/com/yourcompany/web/exception/GlobalExceptionHandler.java import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BaseCustomException.class) public ResponseEntity<ApiResponse<Void>> handleCustomException(BaseCustomException ex) { ApiResponse<Void> response = new ApiResponse<>( ex.getHttpStatus(), ex.getUserMessage(), null ); return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getHttpStatus())); } // Optional: Handle generic exceptions as a fallback @ExceptionHandler(Exception.class) public ResponseEntity<ApiResponse<Void>> handleGenericException(Exception ex) { ApiResponse<Void> response = new ApiResponse<>( HttpStatus.INTERNAL_SERVER_ERROR.value(), "请稍后重试", // Fallback message for unexpected errors null ); return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); } }
3. Throw Custom Exceptions in the Business Layer
Now in your Business layer services, whenever you hit a scenario that should trigger the custom response, throw the corresponding exception. Remember to log the original error details for debugging—never expose that to the UI!
// Business/src/main/java/com/yourcompany/business/service/YourService.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class YourService { private static final Logger log = LoggerFactory.getLogger(YourService.class); public void executeCriticalBusinessLogic() { try { // Your business code that might fail (e.g., database call, third-party API) } catch (Exception ex) { // Log the full error for internal debugging log.error("Critical business logic failed: {}", ex.getMessage(), ex); // Throw custom exception to trigger UI-friendly response throw new ServiceUnavailableException(); } } }
Key Notes to Remember
- Maven Dependencies: Make sure your Business and Web modules have a dependency on the Domain module in their
pom.xml:<!-- In Business/pom.xml and Web/pom.xml --> <dependency> <groupId>com.yourcompany</groupId> <artifactId>domain</artifactId> <version>${project.version}</version> </dependency> - Separation of Concerns: Keep exception definitions in Domain (shared logic), throwing logic in Business (business rules), and response formatting in Web (API layer).
- Extensibility: Add new custom exceptions by extending
BaseCustomExceptionand updating the global handler if needed—this pattern scales easily for more status codes and messages.
This setup ensures that whenever your server hits a specific error condition, the UI gets a clean, user-friendly message paired with the correct HTTP status code—no more generic "internal server error" responses!
内容的提问来源于stack exchange,提问作者Rohan Razdan

