技术小馆专注AI与Java领域的前沿技术知识库 点击进入
重构 Controller 的 7 个黄金法则
你是否曾经面对过一个臃肿不堪的 Controller?方法堆积如山,业务逻辑混乱,测试困难,维护噩梦。当新需求来临时,你不得不在这个"巨无霸"中继续添加代码,让问题雪上加霜。一个优雅的 Controller 应该是轻量级的、职责单一的、易于测试和维护的。它就像一位优雅的舞者,动作简洁而有力,每个转身都恰到好处。

1. Controller 设计的核心原则
单一职责原则(SRP)
一个 Controller 应该只负责处理 HTTP 请求和响应,而不是承载业务逻辑。让我们看一个反例:
less
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody UserRegistrationDto dto) {
// 验证邮箱格式
if (!isValidEmail(dto.getEmail())) {
return ResponseEntity.badRequest().body("Invalid email format");
}
// 检查密码强度
if (!isPasswordStrong(dto.getPassword())) {
return ResponseEntity.badRequest().body("Password too weak");
}
// 检查用户是否已存在
if (userRepository.existsByEmail(dto.getEmail())) {
return ResponseEntity.badRequest().body("User already exists");
}
// 加密密码
String encryptedPassword = passwordEncoder.encode(dto.getPassword());
// 创建用户
User user = new User();
user.setEmail(dto.getEmail());
user.setPassword(encryptedPassword);
user.setName(dto.getName());
// 保存用户
User savedUser = userRepository.save(user);
// 发送欢迎邮件
emailService.sendWelcomeEmail(savedUser.getEmail());
return ResponseEntity.ok(savedUser);
}
// 其他验证方法...
}
这个 Controller 违反了单一职责原则,包含了太多业务逻辑。重构后应该是这样:
less
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody UserRegistrationDto dto) {
try {
User user = userService.registerUser(dto);
return ResponseEntity.ok(user);
} catch (UserRegistrationException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
依赖注入与解耦
通过构造函数注入依赖,而不是直接实例化,这样便于测试和替换实现:
kotlin
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EmailService emailService;
private final UserValidator userValidator;
public UserService(UserRepository userRepository,
PasswordEncoder passwordEncoder,
EmailService emailService,
UserValidator userValidator) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.emailService = emailService;
this.userValidator = userValidator;
}
}
接口隔离原则
为不同的功能定义专门的接口,避免一个接口承担过多职责:
csharp
public interface UserRegistrationService {
User registerUser(UserRegistrationDto dto);
}
public interface UserQueryService {
User findById(Long id);
List<User> findAll();
User findByEmail(String email);
}
public interface UserUpdateService {
User updateUser(Long id, UserUpdateDto dto);
void deleteUser(Long id);
}
2. 代码结构优化策略
方法提取与重构
将长方法拆分为多个小方法,每个方法只做一件事:
scss
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@PostMapping
public ResponseEntity<?> createOrder(@RequestBody CreateOrderDto dto) {
// 验证请求
validateCreateOrderRequest(dto);
// 检查库存
checkInventoryAvailability(dto);
// 创建订单
Order order = createOrderFromDto(dto);
// 保存订单
Order savedOrder = orderService.save(order);
// 发送确认邮件
sendOrderConfirmation(savedOrder);
return ResponseEntity.ok(savedOrder);
}
private void validateCreateOrderRequest(CreateOrderDto dto) {
if (dto.getItems() == null || dto.getItems().isEmpty()) {
throw new InvalidOrderException("Order must contain at least one item");
}
// 其他验证逻辑...
}
private void checkInventoryAvailability(CreateOrderDto dto) {
for (OrderItemDto item : dto.getItems()) {
if (!inventoryService.isAvailable(item.getProductId(), item.getQuantity())) {
throw new InsufficientInventoryException("Insufficient inventory for product: " + item.getProductId());
}
}
}
private Order createOrderFromDto(CreateOrderDto dto) {
Order order = new Order();
order.setCustomerId(dto.getCustomerId());
order.setItems(dto.getItems().stream()
.map(this::createOrderItem)
.collect(Collectors.toList()));
order.setTotalAmount(calculateTotal(dto.getItems()));
return order;
}
}
参数验证与异常处理
使用统一的异常处理机制和参数验证:
less
@RestController
@RequestMapping("/api/products")
public class ProductController {
@PostMapping
public ResponseEntity<?> createProduct(@Valid @RequestBody CreateProductDto dto) {
Product product = productService.createProduct(dto);
return ResponseEntity.ok(product);
}
@PutMapping("/{id}")
public ResponseEntity<?> updateProduct(@PathVariable Long id,
@Valid @RequestBody UpdateProductDto dto) {
try {
Product product = productService.updateProduct(id, dto);
return ResponseEntity.ok(product);
} catch (ProductNotFoundException e) {
return ResponseEntity.notFound().build();
}
}
}
// 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationErrors(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest()
.body(new ErrorResponse("Validation failed", errors));
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<?> handleBusinessException(BusinessException ex) {
return ResponseEntity.badRequest()
.body(new ErrorResponse(ex.getMessage()));
}
}
响应格式标准化
定义统一的响应格式,提高 API 的一致性:
typescript
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
private long timestamp;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(true);
response.setData(data);
response.setTimestamp(System.currentTimeMillis());
return response;
}
public static <T> ApiResponse<T> error(String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(false);
response.setMessage(message);
response.setTimestamp(System.currentTimeMillis());
return response;
}
}
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUser(@PathVariable Long id) {
try {
User user = userService.findById(id);
return ResponseEntity.ok(ApiResponse.success(user));
} catch (UserNotFoundException e) {
return ResponseEntity.ok(ApiResponse.error("User not found"));
}
}
}
3. 业务逻辑分离技巧
Service 层职责划分
将业务逻辑从 Controller 中提取到 Service 层:
scss
@Service
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final ProductService productService;
private final CustomerService customerService;
private final PaymentService paymentService;
public Order createOrder(CreateOrderDto dto) {
// 验证客户
Customer customer = customerService.findById(dto.getCustomerId());
// 验证产品
List<Product> products = validateProducts(dto.getItems());
// 计算价格
BigDecimal totalAmount = calculateTotalAmount(dto.getItems());
// 创建订单
Order order = new Order();
order.setCustomer(customer);
order.setItems(createOrderItems(dto.getItems()));
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatus.PENDING);
// 保存订单
Order savedOrder = orderRepository.save(order);
// 处理支付
paymentService.processPayment(savedOrder);
return savedOrder;
}
private List<Product> validateProducts(List<OrderItemDto> items) {
return items.stream()
.map(item -> productService.findById(item.getProductId()))
.collect(Collectors.toList());
}
private BigDecimal calculateTotalAmount(List<OrderItemDto> items) {
return items.stream()
.map(item -> {
Product product = productService.findById(item.getProductId());
return product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()));
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
数据转换与映射
使用 DTO 和 Mapper 来处理数据传输:
scss
public class UserMapper {
public static UserDto toDto(User user) {
UserDto dto = new UserDto();
dto.setId(user.getId());
dto.setEmail(user.getEmail());
dto.setName(user.getName());
dto.setCreatedAt(user.getCreatedAt());
return dto;
}
public static User toEntity(CreateUserDto dto) {
User user = new User();
user.setEmail(dto.getEmail());
user.setName(dto.getName());
user.setCreatedAt(LocalDateTime.now());
return user;
}
public static List<UserDto> toDtoList(List<User> users) {
return users.stream()
.map(UserMapper::toDto)
.collect(Collectors.toList());
}
}
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public ResponseEntity<ApiResponse<List<UserDto>>> getAllUsers() {
List<User> users = userService.findAll();
List<UserDto> userDtos = UserMapper.toDtoList(users);
return ResponseEntity.ok(ApiResponse.success(userDtos));
}
}
事务管理策略
在 Service 层使用事务注解,确保数据一致性:
scss
@Service
@Transactional
public class OrderService {
@Transactional(readOnly = true)
public Order findById(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new OrderNotFoundException("Order not found: " + id));
}
@Transactional
public Order createOrder(CreateOrderDto dto) {
// 创建订单逻辑
Order order = buildOrder(dto);
// 保存订单
Order savedOrder = orderRepository.save(order);
// 更新库存
updateInventory(dto.getItems());
// 发送通知
notificationService.sendOrderCreatedNotification(savedOrder);
return savedOrder;
}
@Transactional
public void cancelOrder(Long orderId) {
Order order = findById(orderId);
if (order.getStatus() != OrderStatus.PENDING) {
throw new InvalidOrderStatusException("Cannot cancel order in status: " + order.getStatus());
}
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
// 恢复库存
restoreInventory(order.getItems());
// 发送取消通知
notificationService.sendOrderCancelledNotification(order);
}
}
4. 测试友好性设计
单元测试编写
为 Controller 编写单元测试,确保每个方法都能正确工作:
less
@ExtendWith(MockitoExtension.class)
class UserControllerTest {
@Mock
private UserService userService;
@Mock
private UserValidator userValidator;
@InjectMocks
private UserController userController;
@Test
void createUser_WithValidData_ReturnsCreatedUser() {
// Arrange
CreateUserDto dto = new CreateUserDto();
dto.setEmail("test@example.com");
dto.setName("Test User");
User user = new User();
user.setId(1L);
user.setEmail(dto.getEmail());
user.setName(dto.getName());
when(userService.createUser(dto)).thenReturn(user);
// Act
ResponseEntity<ApiResponse<User>> response = userController.createUser(dto);
// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().isSuccess()).isTrue();
assertThat(response.getBody().getData().getEmail()).isEqualTo(dto.getEmail());
verify(userService).createUser(dto);
}
@Test
void createUser_WithInvalidData_ReturnsBadRequest() {
// Arrange
CreateUserDto dto = new CreateUserDto();
dto.setEmail("invalid-email");
when(userService.createUser(dto))
.thenThrow(new ValidationException("Invalid email format"));
// Act
ResponseEntity<ApiResponse<User>> response = userController.createUser(dto);
// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().isSuccess()).isFalse();
assertThat(response.getBody().getMessage()).contains("Invalid email format");
}
}
Mock 策略选择
选择合适的 Mock 策略来隔离依赖:
less
@WebMvcTest(UserController.class)
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void getUser_WithValidId_ReturnsUser() throws Exception {
// Arrange
User user = new User();
user.setId(1L);
user.setEmail("test@example.com");
user.setName("Test User");
when(userService.findById(1L)).thenReturn(user);
// Act & Assert
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.email").value("test@example.com"))
.andExpect(jsonPath("$.data.name").value("Test User"));
}
@Test
void getUser_WithInvalidId_ReturnsNotFound() throws Exception {
// Arrange
when(userService.findById(999L))
.thenThrow(new UserNotFoundException("User not found"));
// Act & Assert
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.message").value("User not found"));
}
}
集成测试设计
设计端到端的集成测试,验证整个流程:
less
@SpringBootTest
@AutoConfigureTestDatabase
@Transactional
class UserControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private UserRepository userRepository;
@Test
void createAndRetrieveUser_CompleteFlow_Success() {
// Arrange
CreateUserDto createDto = new CreateUserDto();
createDto.setEmail("integration@example.com");
createDto.setName("Integration User");
// Act - Create user
ResponseEntity<ApiResponse> createResponse = restTemplate.postForEntity(
"/api/users", createDto, ApiResponse.class);
// Assert - User created
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(createResponse.getBody().isSuccess()).isTrue();
// Act - Retrieve user
ResponseEntity<ApiResponse> getResponse = restTemplate.getForEntity(
"/api/users/1", ApiResponse.class);
// Assert - User retrieved
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(getResponse.getBody().isSuccess()).isTrue();
// Verify database
User savedUser = userRepository.findById(1L).orElse(null);
assertThat(savedUser).isNotNull();
assertThat(savedUser.getEmail()).isEqualTo("integration@example.com");
}
}
5. 性能优化与缓存
响应时间优化
通过异步处理和缓存来提升响应速度:
less
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/{id}")
@Cacheable(value = "products", key = "#id")
public ResponseEntity<ApiResponse<Product>> getProduct(@PathVariable Long id) {
Product product = productService.findById(id);
return ResponseEntity.ok(ApiResponse.success(product));
}
@GetMapping("/search")
public ResponseEntity<ApiResponse<List<Product>>> searchProducts(
@RequestParam String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<Product> products = productService.searchByKeyword(keyword, pageable);
return ResponseEntity.ok(ApiResponse.success(products.getContent()));
}
@PostMapping("/{id}/view")
@Async
public void incrementViewCount(@PathVariable Long id) {
productService.incrementViewCount(id);
}
}
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product findById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new ProductNotFoundException("Product not found: " + id));
}
@CacheEvict(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
@Async
public void incrementViewCount(Long productId) {
productRepository.incrementViewCount(productId);
}
}
缓存策略实施
实现多层次的缓存策略:
typescript
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
ConcurrentMapCache productsCache = new ConcurrentMapCache("products");
ConcurrentMapCache usersCache = new ConcurrentMapCache("users");
ConcurrentMapCache ordersCache = new ConcurrentMapCache("orders");
cacheManager.setCaches(Arrays.asList(productsCache, usersCache, ordersCache));
return cacheManager;
}
}
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product findById(Long id) {
return productRepository.findById(id).orElse(null);
}
@Cacheable(value = "products", key = "'search_' + #keyword + '_' + #pageable.pageNumber + '_' + #pageable.pageSize")
public Page<Product> searchByKeyword(String keyword, Pageable pageable) {
return productRepository.findByNameContainingIgnoreCase(keyword, pageable);
}
@CacheEvict(value = "products", allEntries = true)
public void clearAllCaches() {
// 清除所有产品相关缓存
}
}
异步处理机制
使用异步处理来处理耗时操作:
scss
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@PostMapping("/{id}/process")
public ResponseEntity<ApiResponse<String>> processOrder(@PathVariable Long id) {
// 立即返回处理中的状态
orderService.processOrderAsync(id);
return ResponseEntity.ok(ApiResponse.success("Order processing started"));
}
@GetMapping("/{id}/status")
public ResponseEntity<ApiResponse<OrderStatus>> getOrderStatus(@PathVariable Long id) {
OrderStatus status = orderService.getOrderStatus(id);
return ResponseEntity.ok(ApiResponse.success(status));
}
}
@Service
public class OrderService {
@Async
public void processOrderAsync(Long orderId) {
try {
// 模拟耗时处理
Thread.sleep(5000);
// 更新订单状态
Order order = findById(orderId);
order.setStatus(OrderStatus.PROCESSING);
orderRepository.save(order);
// 继续处理...
processPayment(order);
updateInventory(order);
sendNotifications(order);
order.setStatus(OrderStatus.COMPLETED);
orderRepository.save(order);
} catch (Exception e) {
// 处理异常,更新订单状态为失败
Order order = findById(orderId);
order.setStatus(OrderStatus.FAILED);
orderRepository.save(order);
log.error("Failed to process order: " + orderId, e);
}
}
}
6. 代码规范与文档
命名规范制定
建立清晰的命名约定:
less
// Controller 命名:以 Controller 结尾,使用复数形式
@RestController
@RequestMapping("/api/users")
public class UserController { }
@RestController
@RequestMapping("/api/orders")
public class OrderController { }
// 方法命名:使用动词开头,清晰表达意图
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUserById(@PathVariable Long id) { }
@PostMapping
public ResponseEntity<ApiResponse<User>> createNewUser(@RequestBody CreateUserDto dto) { }
@PutMapping("/{id}")
public ResponseEntity<ApiResponse<User>> updateExistingUser(@PathVariable Long id,
@RequestBody UpdateUserDto dto) { }
@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> removeUser(@PathVariable Long id) { }
// DTO 命名:以 Dto 结尾,使用描述性名称
public class CreateUserDto { }
public class UpdateUserDto { }
public class UserResponseDto { }
// 异常命名:以 Exception 结尾
public class UserNotFoundException extends RuntimeException { }
public class InvalidUserDataException extends RuntimeException { }
API 文档生成
使用 Swagger 或 OpenAPI 生成 API 文档:
less
@RestController
@RequestMapping("/api/users")
@Tag(name = "User Management", description = "APIs for managing users")
public class UserController {
@Operation(summary = "Create a new user", description = "Creates a new user with the provided information")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "User created successfully",
content = @Content(schema = @Schema(implementation = User.class))),
@ApiResponse(responseCode = "400", description = "Invalid input data"),
@ApiResponse(responseCode = "409", description = "User already exists")
})
@PostMapping
public ResponseEntity<ApiResponse<User>> createUser(
@Parameter(description = "User information", required = true)
@Valid @RequestBody CreateUserDto dto) {
User user = userService.createUser(dto);
return ResponseEntity.ok(ApiResponse.success(user));
}
@Operation(summary = "Get user by ID", description = "Retrieves a user by their unique identifier")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "User found successfully"),
@ApiResponse(responseCode = "404", description = "User not found")
})
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUserById(
@Parameter(description = "User ID", required = true)
@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(ApiResponse.success(user));
}
}
代码审查要点
建立代码审查检查清单:
less
// 代码审查检查清单示例
/*
□ Controller 是否只负责 HTTP 请求处理?
□ 业务逻辑是否已提取到 Service 层?
□ 是否使用了适当的 HTTP 状态码?
□ 异常处理是否统一?
□ 参数验证是否完整?
□ 响应格式是否一致?
□ 是否添加了适当的日志?
□ 方法是否过于复杂(超过 20 行)?
□ 是否遵循了命名约定?
□ 是否添加了必要的注释?
□ 是否考虑了性能问题?
□ 是否添加了单元测试?
*/
@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
@PostMapping
public ResponseEntity<ApiResponse<User>> createUser(@Valid @RequestBody CreateUserDto dto) {
log.info("Creating new user with email: {}", dto.getEmail());
try {
User user = userService.createUser(dto);
log.info("User created successfully with ID: {}", user.getId());
return ResponseEntity.ok(ApiResponse.success(user));
} catch (UserAlreadyExistsException e) {
log.warn("Failed to create user: {}", e.getMessage());
return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
} catch (Exception e) {
log.error("Unexpected error while creating user", e);
return ResponseEntity.ok(ApiResponse.error("Internal server error"));
}
}
}
7. 重构实战案例
遗留代码分析
分析一个典型的"巨无霸" Controller:
scss
// 重构前的混乱 Controller
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
@Autowired
private PaymentService paymentService;
@PostMapping
public ResponseEntity<?> createOrder(@RequestBody Map<String, Object> request) {
try {
// 验证用户
Long userId = Long.valueOf(request.get("userId").toString());
User user = userRepository.findById(userId).orElse(null);
if (user == null) {
return ResponseEntity.badRequest().body("User not found");
}
// 验证产品
List<Map<String, Object>> items = (List<Map<String, Object>>) request.get("items");
List<OrderItem> orderItems = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
for (Map<String, Object> item : items) {
Long productId = Long.valueOf(item.get("productId").toString());
Integer quantity = Integer.valueOf(item.get("quantity").toString());
Product product = productRepository.findById(productId).orElse(null);
if (product == null) {
return ResponseEntity.badRequest().body("Product not found: " + productId);
}
if (product.getStock() < quantity) {
return ResponseEntity.badRequest().body("Insufficient stock for product: " + productId);
}
OrderItem orderItem = new OrderItem();
orderItem.setProduct(product);
orderItem.setQuantity(quantity);
orderItem.setPrice(product.getPrice());
orderItems.add(orderItem);
totalAmount = totalAmount.add(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
// 更新库存
product.setStock(product.getStock() - quantity);
productRepository.save(product);
}
// 创建订单
Order order = new Order();
order.setUser(user);
order.setItems(orderItems);
order.setTotalAmount(totalAmount);
order.setStatus("PENDING");
order.setCreatedAt(new Date());
Order savedOrder = orderRepository.save(order);
// 处理支付
String paymentResult = paymentService.processPayment(savedOrder);
if (!"SUCCESS".equals(paymentResult)) {
// 回滚库存
for (OrderItem item : orderItems) {
Product product = item.getProduct();
product.setStock(product.getStock() + item.getQuantity());
productRepository.save(product);
}
orderRepository.delete(savedOrder);
return ResponseEntity.badRequest().body("Payment failed");
}
// 发送确认邮件
emailService.sendOrderConfirmation(user.getEmail(), savedOrder);
return ResponseEntity.ok(savedOrder);
} catch (Exception e) {
return ResponseEntity.status(500).body("Internal server error: " + e.getMessage());
}
}
// 其他混乱的方法...
}
重构计划制定
制定分步骤的重构计划:
less
// 第一步:提取 DTO 和验证
public class CreateOrderDto {
@NotNull
private Long userId;
@NotEmpty
@Valid
private List<OrderItemDto> items;
// getters and setters
}
public class OrderItemDto {
@NotNull
private Long productId;
@Min(1)
private Integer quantity;
// getters and setters
}
// 第二步:创建专门的 Service
@Service
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final ProductService productService;
private final UserService userService;
private final PaymentService paymentService;
private final EmailService emailService;
public Order createOrder(CreateOrderDto dto) {
// 验证用户
User user = userService.findById(dto.getUserId());
// 验证产品并创建订单项
List<OrderItem> orderItems = createOrderItems(dto.getItems());
// 计算总金额
BigDecimal totalAmount = calculateTotalAmount(orderItems);
// 创建订单
Order order = buildOrder(user, orderItems, totalAmount);
// 保存订单
Order savedOrder = orderRepository.save(order);
// 处理支付
processPayment(savedOrder);
// 发送确认邮件
emailService.sendOrderConfirmation(user.getEmail(), savedOrder);
return savedOrder;
}
private List<OrderItem> createOrderItems(List<OrderItemDto> itemDtos) {
return itemDtos.stream()
.map(this::createOrderItem)
.collect(Collectors.toList());
}
private OrderItem createOrderItem(OrderItemDto dto) {
Product product = productService.findById(dto.getProductId());
productService.checkStockAvailability(product, dto.getQuantity());
OrderItem item = new OrderItem();
item.setProduct(product);
item.setQuantity(dto.getQuantity());
item.setPrice(product.getPrice());
return item;
}
}
// 第三步:重构 Controller
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<ApiResponse<Order>> createOrder(@Valid @RequestBody CreateOrderDto dto) {
try {
Order order = orderService.createOrder(dto);
return ResponseEntity.ok(ApiResponse.success(order));
} catch (BusinessException e) {
return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
}
}
}
重构后效果评估
评估重构的效果和改进:
scss
// 重构后的效果对比
/*
重构前的问题:
- Controller 方法超过 80 行
- 混合了业务逻辑、数据访问、验证等职责
- 异常处理不统一
- 难以测试和维护
- 代码重复严重
重构后的改进:
- Controller 方法只有 10 行左右
- 职责分离清晰
- 统一的异常处理
- 易于测试和维护
- 代码复用性高
- 符合 SOLID 原则
*/
// 重构后的测试覆盖
@ExtendWith(MockitoExtension.class)
class OrderControllerTest {
@Mock
private OrderService orderService;
@InjectMocks
private OrderController orderController;
@Test
void createOrder_WithValidData_ReturnsSuccess() {
// Arrange
CreateOrderDto dto = new CreateOrderDto();
dto.setUserId(1L);
dto.setItems(Arrays.asList(new OrderItemDto(1L, 2)));
Order order = new Order();
order.setId(1L);
when(orderService.createOrder(dto)).thenReturn(order);
// Act
ResponseEntity<ApiResponse<Order>> response = orderController.createOrder(dto);
// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().isSuccess()).isTrue();
assertThat(response.getBody().getData().getId()).isEqualTo(1L);
}
}
// 重构后的性能指标
/*
- 响应时间:从平均 500ms 降低到 200ms
- 代码行数:从 200+ 行减少到 50 行
- 测试覆盖率:从 30% 提升到 85%
- 维护成本:降低 60%
- 代码质量评分:从 C 提升到 A
*/