共计 2825 个字符,预计需要花费 8 分钟才能阅读完成。
糟糕代码的典型问题
最近接手一个老项目的用户注册功能,代码让人头皮发麻:

- 200 行的 Controller 方法:注册逻辑、参数校验、数据库操作全堆在一起
- 重复的密码加密代码:在三个地方看到相同的 MD5 加密逻辑
- 零文档:前端同事需要不断打电话询问接口字段含义
- 魔法数字泛滥 :各种
if(status == 3)散落各处
这样的代码每次修改都像走钢丝,稍有不慎就会引发连锁问题。接下来让我们用现代后端开发的最佳实践重构这个功能。
分层架构设计
1. 三层结构划分
-
Controller 层:处理 HTTP 请求 / 响应,不做业务逻辑
@RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @Operation(summary = "用户注册") @PostMapping("/register") public ResponseEntity<UserVO> register(@Valid @RequestBody RegisterDTO dto) {return ResponseEntity.ok(userService.register(dto)); } } -
Service 层:核心业务逻辑,保持无状态
@Service public class UserServiceImpl implements UserService { @Override public UserVO register(RegisterDTO dto) { // 业务逻辑组合 checkDuplicateUsername(dto.getUsername()); User user = convertToEntity(dto); userRepository.save(user); return convertToVO(user); } } -
DAO 层:纯粹的数据访问,使用 Spring Data JPA 或 MyBatis
2. 使用 Lombok 简化代码
实体类可以简化到极致:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class User {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String encryptedPassword;
}
RESTful API 实践
1. 标准响应结构
建议统一响应格式:
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {return new Result<>(200, "成功", data);
}
}
2. Swagger 集成
在 pom.xml 添加依赖后:
@Configuration
@OpenAPIDefinition(info = @Info(title = "用户服务 API", version = "1.0"))
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {return new OpenAPI().addSecurityItem(new SecurityRequirement().addList("JWT"));
}
}
访问 /swagger-ui.html 即可获得交互式文档
避坑指南
1. 解决 N + 1 查询
错误的写法:
// Service 中
List<User> users = userRepository.findAll();
users.forEach(user -> {
// 每次循环都会查询数据库
List<Order> orders = orderRepository.findByUserId(user.getId());
});
正确方案:
// 使用 JPA 的 @EntityGraph
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
2. 密码安全存储
永远不要这样做:
// 致命错误!user.setPassword(password);
应该使用 BCrypt:
@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}
// 使用时
user.setEncryptedPassword(passwordEncoder.encode(rawPassword));
3. 日志规范
推荐使用 SLF4J:
private static final Logger log = LoggerFactory.getLogger(UserService.class);
void someMethod() {log.info("用户注册开始,username={}", username);
try {// ...} catch (Exception e) {log.error("注册异常", e); // 一定要打印堆栈
}
}
单元测试示例
使用 MockMvc 测试 Controller:
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void registerSuccess() throws Exception {mockMvc.perform(post("/api/user/register")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"username\":\"test\",\"password\":\"123456\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.username").value("test"));
}
}
思考与进阶
当 API 需要兼容多版本时,可以考虑:
- URL 路径版本:
/v1/api/user - 请求头版本:
Accept: application/vnd.myapi.v1+json - 参数版本:
/api/user?version=1
推荐进一步学习:
– 领域驱动设计(DDD):通过限界上下文划分复杂系统
– CQRS 模式:将读写操作分离以获得更高性能
好的代码不是一蹴而就的,建议每次提交前问自己三个问题:
1. 这段代码半年后还能看懂吗?
2. 别人修改这里是否容易出错?
3. 出现异常时能否快速定位问题?
保持这种习惯,你的代码质量会不断提升。
正文完
发表至: 编程开发
近一天内
