Skip to content

1. Controller 层(控制层/表现层)

详细职责

路由处理:接收 HTTP 请求(GET/POST/PUT/DELETE),根据 URL 映射到具体方法。 • 参数解析:从请求体(@RequestBody)、路径(@PathVariable)、查询参数(@RequestParam)中提取数据。 • 数据校验:使用 @Valid 注解配合 Hibernate Validator 进行参数校验(如 @NotNull, @Size)。 • 响应封装:将 Service 层返回的数据包装为统一格式(如 Result<T> 包含 code、message、data)。

代码示例

java
@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 或外部服务(如支付接口、消息队列)。

代码示例

java
@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(如 UserServiceOrderService)。

常见问题

事务失效:非 public 方法、自调用(通过代理对象调用解决)。 • 业务逻辑臃肿:Service 方法过长(可拆分为多个私有方法或领域对象)。


3. Model 层(实体层/领域层)

详细职责

数据建模:定义与数据库表映射的实体类(JPA/Hibernate 的 @Entity)。 • 领域行为:在 DDD 中,实体类可能包含业务方法(如 Order.calculateTotal())。

代码示例

java
@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(如 UserCreateRequestUserDetailResponse)。

代码示例

java
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 层(工具层)

详细职责

全局复用逻辑:如日期格式化、加密解密、集合操作。

代码示例

java
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 密钥。

代码示例

java
@Configuration
public class AppConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
yaml
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456

最佳实践

环境隔离:使用 application-dev.ymlapplication-prod.yml 区分配置。 • 敏感信息加密:使用 Jasypt 加密数据库密码。

常见问题

配置冲突:不同环境的配置未正确加载(检查 spring.profiles.active)。 • Bean 循环依赖:通过构造函数注入替代字段注入解决。


7. Exception 层(异常处理层)

详细职责

统一异常处理:捕获 Controller 层抛出的异常,返回友好错误信息。 • 自定义异常:定义 BusinessException 区分系统异常和业务异常。

代码示例

java
@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):

java
@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):

java
@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。

代码示例

java
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 方法或参数化查询。 • 分页查询:使用 PageablePage<T> 返回分页结果。

常见问题

N+1 查询:懒加载关联对象导致多次查询(通过 @EntityGraph 或 Fetch Join 解决)。 • 方法名过长:如 findByFirstNameAndLastNameOrEmail(可拆分或使用 @Query)。


11. AOP 层(切面层)

详细职责

横切关注点:如日志记录、性能监控、缓存、权限校验。

代码示例(日志切面):

java
@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 缓存热点数据,减轻数据库压力。 • 注意:缓存穿透、雪崩、击穿问题的解决方案(如布隆过滤器、互斥锁)。


总结:分层架构的核心原则

  1. 单一职责:每层只做一件事(如 Controller 处理请求,Service 处理业务)。
  2. 依赖方向:高层模块依赖低层模块(如 Controller → Service → DAO)。
  3. 解耦与扩展:通过接口隔离变化(如替换数据库只需修改 DAO 实现)。
  4. 测试友好:分层后更容易编写单元测试和集成测试。

通过清晰的分层,可以大幅提升代码的可维护性、可测试性和团队协作效率。

✨ 网站运行时间: 3年11月15天 ❤️ 道阻且长,行则将至 - 微信号: heikedreamer