什么是 Spring MVC 三层架构?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

面试官提出这个问题,通常旨在考察以下几点:

  1. 对经典软件架构思想的理解:不仅仅是想知道三个层的名称,更是想知道你是否理解 “关注点分离”、“高内聚低耦合” 这些核心设计原则,以及它们如何提升代码的可维护性、可测试性和可扩展性。
  2. 对 Spring MVC 中各层职责的清晰认知:你是否能准确描述每一层(Controller, Service, Dao/Repository)应该做什么、不应该做什么,以及它们之间如何协作。
  3. 理论与框架实践的结合能力:你是否能将这种普适的架构思想与 Spring MVC(或 Spring Boot)框架的具体组件(如 @Controller, @Service, @Repository)联系起来,并说明框架是如何支持这种分层架构的。
  4. 辨别常见误区:是否能指出在项目中常见的分层错误,例如业务逻辑侵入 Controller 层、Service 层变成“透传层”等,这能反映出你的实战经验。

核心答案

Spring MVC 三层架构是构建基于 Spring 框架的 Web 应用程序时,一种被广泛采用的、约定俗成的分层设计思想。它将应用程序的职责纵向切分为三个核心层次:

  1. 表示层/控制层:通常对应 Spring MVC 中的 Controller。它负责接收 HTTP 请求、解析参数、调用业务层处理,并最终封装响应数据(JSON/HTML 等)返回给客户端。
  2. 业务逻辑层:通常对应 Service。它是应用程序的核心,负责实现具体的业务规则、业务流程、事务管理和领域逻辑。它是连接表示层和数据访问层的桥梁。
  3. 数据访问层:通常对应 DaoRepository。它负责与数据库、缓存、文件系统等持久化存储进行交互,封装所有数据访问细节,提供简洁的 API 供业务层调用。

其核心协作流程是:请求 -> Controller -> Service -> Dao -> 数据库 -> 返回数据 -> Service(业务处理)-> Controller(封装响应)-> 响应

深度解析

原理与机制

三层架构的驱动力是 “关注点分离”。每一层都有其高度内聚的职责,层与层之间通过明确定义的接口进行通信,从而降低耦合度。

  • 数据流向:请求和数据通常自上而下流动(Controller -> Service -> Dao),而结果自下而上返回。
  • 依赖方向:这是一种典型的上层依赖下层的架构。Controller 依赖 Service,Service 依赖 Dao。下层对上层一无所知,这保证了核心业务逻辑和数据访问逻辑的独立性。
  • Spring 的支撑:Spring 框架通过 依赖注入注解(如 @Controller, @Service, @Repository)完美地实现了各层组件的创建、管理和装配,使这种分层模式易于实施。

代码示例

以下是一个管理用户的简单示例,展示了各层的典型代码结构:

1. 数据访问层:使用 @Repository 标识。

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Spring Data JPA 会自动实现此方法
    Optional<User> findByUsername(String username);
}

2. 业务逻辑层:使用 @Service 标识,并注入 Repository

@Service
@Transactional // 事务管理通常放在 Service 层
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public UserDTO getUserProfile(String username) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
        // 进行业务逻辑处理,例如计算、验证、组合数据等
        return convertToDTO(user);
    }

    private UserDTO convertToDTO(User user) {
        // 转换实体为 DTO
        return new UserDTO(user.getId(), user.getUsername(), user.getEmail());
    }
}

3. 表示层/控制层:使用 @RestController 标识,并注入 Service

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{username}/profile")
    public ResponseEntity<UserDTO> getProfile(@PathVariable String username) {
        UserDTO userProfile = userService.getUserProfile(username);
        // Controller 主要做请求/响应的协调,不应包含复杂业务逻辑
        return ResponseEntity.ok(userProfile);
    }
}

注:UserDTO 是一个数据传输对象,用于在层间传递数据,避免直接暴露领域模型或数据库实体。

对比分析与最佳实践

  • 三层架构 vs MVC 模式
    • MVC 是一种表现层的设计模式,主要解决用户界面(View)与数据模型(Model)和用户交互逻辑(Controller)的分离问题。
    • 三层架构 是整个应用程序的纵向架构,它涵盖了表现层、业务层和数据层。在 Spring MVC 中,我们常说的 “Controller” 对应 MVC 中的 “C”,而 “Service + Dao” 共同构成了 MVC 中更广义的 “Model”。
  • 最佳实践
    1. 严守职责边界:Controller 只做路由、参数校验和响应封装;Service 承载核心业务逻辑;Dao 只做数据操作。
    2. 使用 DTO/VO 进行层间数据传输:避免将数据库实体(如 User JPA Entity)直接传到 Controller 层或返回给前端,以防止暴露内部字段或引起惰性加载异常。
    3. 事务管理应在 Service 层:利用 Spring 的 @Transactional 在 Service 方法上声明事务,保证业务操作的原子性。
    4. 避免 “贫血模型”:在复杂业务系统中,警惕 Service 层变得过于庞大。可以考虑引入领域驱动设计,将部分业务逻辑富化到领域实体中。

常见误区

  1. 在 Controller 中编写业务逻辑:这是最常见的反模式,会导致 Controller 臃肿,业务逻辑无法复用,且难以测试。
  2. Service 层变成“透传层”:如果 Service 方法只是简单地调用一下 Dao 方法然后返回,那就失去了它存在的价值,需要思考是否业务逻辑缺失或被错误地放置了。
  3. 层与层之间紧耦合:例如,在 Service 层直接使用 HttpServletRequest 等 Web 相关对象,这使得 Service 无法脱离 Web 环境进行单元测试。

总结

Spring MVC 三层架构是一种通过分层实现关注点分离的经典实践,它利用 Spring 框架的 IOC 和注解支持,由 ControllerServiceDao 各司其职,共同构建出结构清晰、易于维护和测试的企业级应用。