1. Controller 层(控制层/表现层)
详细职责:
• 路由处理:接收 HTTP 请求(GET/POST/PUT/DELETE),根据 URL 映射到具体方法。 • 参数解析:从请求体(@RequestBody)、路径(@PathVariable)、查询参数(@RequestParam)中提取数据。 • 数据校验:使用 @Valid 注解配合 Hibernate Validator 进行参数校验(如 @NotNull, @Size)。 • 响应封装:将 Service 层返回的数据包装为统一格式(如 Result<T> 包含 code、message、data)。
代码示例:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public Result<UserDTO> createUser(@Valid @RequestBody UserRequest request) {
UserDTO user = userService.createUser(request);
return Result.success(user);
}
@GetMapping("/{id}")
public Result<UserDTO> getUser(@PathVariable Long id) {
return Result.success(userService.getUserById(id));
}
// 局部异常处理示例
@ExceptionHandler(BusinessException.class)
public Result<Void> handleException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
}最佳实践:
• 保持简洁:Controller 应只负责流程调度,避免包含业务逻辑。 • 统一响应格式:使用全局拦截器或 @ControllerAdvice 统一处理异常和响应。 • 使用 DTO 而非 Entity:避免直接暴露数据库实体,防止敏感字段泄露。
常见问题:
• 循环依赖:Controller 调用其他 Controller(应通过 Service 层解耦)。 • 参数校验遗漏:忘记在 DTO 中添加 @Valid 导致校验失效。
2. Service 层(服务层/业务逻辑层)
详细职责:
• 业务逻辑实现:处理核心业务流程(如用户注册、订单支付)。 • 事务管理:通过 @Transactional 注解保证数据库操作的原子性。 • 依赖协调:调用多个 DAO 或外部服务(如支付接口、消息队列)。
代码示例:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private EmailService emailService;
@Override
@Transactional(rollbackFor = Exception.class)
public UserDTO createUser(UserRequest request) {
// 业务逻辑:检查用户名是否重复
if (userDao.existsByUsername(request.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 数据转换:DTO → Entity
User user = UserConverter.toEntity(request);
userDao.save(user);
// 调用外部服务(如发送邮件)
emailService.sendWelcomeEmail(user.getEmail());
return UserConverter.toDTO(user);
}
}最佳实践:
• 接口与实现分离:定义 UserService 接口,由 UserServiceImpl 实现(便于扩展和 Mock 测试)。 • 事务粒度控制:在 Service 方法上添加事务,而非 DAO 层。 • 避免“上帝类”:按功能拆分 Service(如 UserService、OrderService)。
常见问题:
• 事务失效:非 public 方法、自调用(通过代理对象调用解决)。 • 业务逻辑臃肿:Service 方法过长(可拆分为多个私有方法或领域对象)。
3. Model 层(实体层/领域层)
详细职责:
• 数据建模:定义与数据库表映射的实体类(JPA/Hibernate 的 @Entity)。 • 领域行为:在 DDD 中,实体类可能包含业务方法(如 Order.calculateTotal())。
代码示例:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
// 省略 getter/setter
}最佳实践:
• 贫血模型 vs 充血模型: • 贫血模型:仅包含数据字段(需在 Service 中处理逻辑)。 • 充血模型:实体类封装数据和业务方法(DDD 推荐)。 • 避免暴露字段:使用 Lombok @Data 或手动生成 getter/setter。
常见问题:
• 实体类污染:添加过多与数据库无关的字段(应拆分为 DTO)。 • JPA 懒加载异常:在事务外访问关联对象(通过 @Transactional 或 FetchType.EAGER 解决)。
4. DTO 层(数据传输对象)
详细职责:
• 数据隔离:防止实体类直接暴露给前端(如隐藏 password 字段)。 • 字段定制:按场景定义不同 DTO(如 UserCreateRequest、UserDetailResponse)。
代码示例:
public class UserDTO {
private Long id;
private String username;
private String email;
// 省略 getter/setter
}
public class UserCreateRequest {
@NotBlank
private String username;
@NotBlank
@Size(min = 6)
private String password;
}最佳实践:
• 使用 MapStruct 或 ModelMapper:自动转换 Entity 与 DTO。 • 分层验证:在 Controller 校验 @Valid,在 Service 校验业务规则。
常见问题:
• DTO 膨胀:创建过多相似的 DTO(可用继承或组合优化)。 • 转换遗漏:手动转换时漏字段(推荐使用自动化工具)。
5. Util 层(工具层)
详细职责:
• 全局复用逻辑:如日期格式化、加密解密、集合操作。
代码示例:
public final class DateUtils {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static String formatDate(Date date) {
return sdf.format(date);
}
// 避免实例化工具类
private DateUtils() {}
}最佳实践:
• 无状态设计:工具类应为静态方法,不保存任何状态。 • 线程安全:避免使用 SimpleDateFormat(可用 ThreadLocal 或 DateTimeFormatter)。
常见问题:
• 工具类滥用:将业务逻辑放入工具类(应属于 Service 层)。 • 重复造轮子:优先使用 Apache Commons、Guava 等成熟库。
6. Config 层(配置层)
详细职责:
• Bean 定义:通过 @Configuration 配置 Spring Bean。 • 外部化配置:从 application.yml 读取数据库连接、第三方 API 密钥。
代码示例:
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456最佳实践:
• 环境隔离:使用 application-dev.yml、application-prod.yml 区分配置。 • 敏感信息加密:使用 Jasypt 加密数据库密码。
常见问题:
• 配置冲突:不同环境的配置未正确加载(检查 spring.profiles.active)。 • Bean 循环依赖:通过构造函数注入替代字段注入解决。
7. Exception 层(异常处理层)
详细职责:
• 统一异常处理:捕获 Controller 层抛出的异常,返回友好错误信息。 • 自定义异常:定义 BusinessException 区分系统异常和业务异常。
代码示例:
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
@ResponseBody
@ExceptionHandler(Exception.class)
public Result<Void> handleUnexpectedException(Exception e) {
return Result.error(500, "系统繁忙");
}
}最佳实践:
• 错误码规范:定义全局错误码表(如 4001 表示“用户不存在”)。 • 日志记录:在全局异常处理中记录错误堆栈。
常见问题:
• 异常吞噬:捕获异常后未打印日志(导致问题难以排查)。 • 过度捕获:在底层捕获异常导致上层无法处理。
8. Security 层(安全层)
详细职责:
• 身份认证(Authentication):验证用户身份(如用户名密码、JWT)。 • 权限控制(Authorization):通过角色或权限限制资源访问。
代码示例(Spring Security):
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtFilter jwtFilter() {
return new JwtFilter();
}
}最佳实践:
• 使用 HTTPS:生产环境强制启用 HTTPS。 • 密码加密存储:使用 BCryptPasswordEncoder 替代明文存储。
常见问题:
• CSRF 防护:未关闭 CSRF 导致 POST 请求被拒绝(对 API 服务可禁用)。 • 权限配置错误:路径匹配顺序错误(antMatchers 按声明顺序生效)。
9. Client 层(外部服务调用层)
详细职责:
• 封装第三方 API:如调用支付网关、短信服务、天气接口。 • 熔断降级:通过 Hystrix 或 Resilience4j 处理服务不可用。
代码示例(Feign Client):
@FeignClient(name = "payment-service", url = "${payment.service.url}")
public interface PaymentClient {
@PostMapping("/pay")
PaymentResponse pay(@RequestBody PaymentRequest request);
}最佳实践:
• 超时设置:配置合理的连接超时和读取超时。 • 重试机制:对幂等操作(如 GET)添加重试,非幂等操作(如 POST)避免重试。
常见问题:
• 接口变化:第三方 API 升级导致 Client 报错(通过契约测试预防)。 • 性能瓶颈:未做异步调用导致线程阻塞(使用 CompletableFuture 或 Reactive 编程)。
10. Repository 层(仓储层)
详细职责:
• 数据访问接口:通过 Spring Data JPA 定义 findByUsername(String username) 等方法。 • 复杂查询:使用 @Query 注解或 QueryDSL 编写 JPQL/SQL。
代码示例:
public interface UserRepository extends JpaRepository<User, Long> {
// 自动生成查询方法
Optional<User> findByUsername(String username);
// 自定义 JPQL
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findByEmailDomain(@Param("domain") String domain);
}最佳实践:
• 避免 SQL 注入:优先使用 JPA 方法或参数化查询。 • 分页查询:使用 Pageable 和 Page<T> 返回分页结果。
常见问题:
• N+1 查询:懒加载关联对象导致多次查询(通过 @EntityGraph 或 Fetch Join 解决)。 • 方法名过长:如 findByFirstNameAndLastNameOrEmail(可拆分或使用 @Query)。
11. AOP 层(切面层)
详细职责:
• 横切关注点:如日志记录、性能监控、缓存、权限校验。
代码示例(日志切面):
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName + " executed in " + duration + "ms");
return result;
}
}最佳实践:
• 切面粒度控制:避免拦截过广(如 execution(* com.example..*.*(..)) 可能影响性能)。 • 优先级管理:通过 @Order 控制多个切面的执行顺序。
常见问题:
• 切面不生效:未启用 AOP(检查 @EnableAspectJAutoProxy)。 • 环绕通知未调用 proceed():导致目标方法未执行。
其他扩展层与概念:
网关层(API Gateway):
• 职责:在微服务中处理路由、鉴权、限流、请求聚合。 • 工具:Spring Cloud Gateway、Kong、Nginx。
消息队列层:
• 职责:解耦服务,实现异步通信(如订单创建后发送消息通知库存系统)。 • 工具:Kafka、RabbitMQ、RocketMQ。
缓存层:
• 职责:通过 Redis、Memcached 缓存热点数据,减轻数据库压力。 • 注意:缓存穿透、雪崩、击穿问题的解决方案(如布隆过滤器、互斥锁)。
总结:分层架构的核心原则
- 单一职责:每层只做一件事(如 Controller 处理请求,Service 处理业务)。
- 依赖方向:高层模块依赖低层模块(如 Controller → Service → DAO)。
- 解耦与扩展:通过接口隔离变化(如替换数据库只需修改 DAO 实现)。
- 测试友好:分层后更容易编写单元测试和集成测试。
通过清晰的分层,可以大幅提升代码的可维护性、可测试性和团队协作效率。