模式解析与 Spring Boot 实现指南)
示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载Page Controller页面控制器是 Web 应用中最基础也最实用的表现层模式之一它让“一个页面”对应“一个逻辑控制器”由控制器统一处理该页的 HTTP 请求并决定使用哪个模型Model与哪个视图View本质上是 MVC 思想在单页场景下的落地。本文以 java-design-patterns 开源仓库中的 page-controller 模块为研究对象结合完整源码与单元测试带你从零读懂该模式的结构、请求流转、适用场景与工程权衡并掌握可直接复用的 Spring Boot 实现方案。1. 模式定位名称、分类与核心目的名称NamePage Controller页面控制器分类Category结构型Structural核心标签Tags解耦Decoupling目的PurposePage Controller 是一种“由单个前端页面导向单个逻辑文件”的设计思路这个逻辑文件负责管理该页面在网站中触发的各类动作或请求。在 Web 应用里每个页面或动作需要特定的请求处理逻辑校验输入、执行业务逻辑再决定返回哪个视图渲染响应。Page Controller 正是为这一需求而生的它将“请求处理”与“视图渲染”两类职责清晰分开。在 java-design-patterns 仓库中该模式位于 page-controller 模块其模块描述直接点明了用途“当我们想要简化动态网站中页面与逻辑之间的关系时使用 Page Controller 模式”——一个前端页面对应一个处理 HTTP 请求与动作的逻辑文件。2. 核心思想一个页面对应一个控制器的 MVC 落地2.1 真实业务场景在一个购物网站上有一个用于注册用户资料的注册页面。当用户完成注册后注册页会重定向到用户信息页展示刚刚注册的资料。这是一个非常典型的 Page Controller 应用场景注册页与用户页各自拥有独立的控制器分别处理自己页面上的请求注册页控制器处理“提交表单”的动作用户页控制器处理“展示用户信息”的请求。两个控制器各司其职、互不干扰页面之间的跳转通过重定向完成。2.2 一句话概括页面控制器管理特定页面上的 HTTP 请求与数据其核心思想是一个页面包含一个控制器该控制器负责处理模型Model与视图View。也就是说每个页面不再是零散的 Servlet JSP 堆砌而是以一个控制器对象为枢纽把“数据Model”“页面View”和“请求处理Controller”三者组织成一个高内聚的单元。3. 请求处理架构与流程图仓库 page-controller/etc 目录 中的架构图清晰地展示了该模式在运行时的完整请求流转从图中可以归纳出五个关键步骤Sends Request客户端Client如浏览器向 Page Controller 发送 HTTP 请求Update ModelPage Controller 接收请求后更新 Model封装业务数据Get DataView 需要渲染数据时向 Model 获取数据RendersPage Controller 完成模型处理后指示 View 进行页面渲染Displays ResponseView 将渲染结果作为响应返回给客户端展示。这套流程体现了 Page Controller 的核心价值所有请求都先汇聚到控制器再由控制器统一调度模型与视图客户端永远不会直接触碰模型或视图内部的细节。4. 源码实战注册页 用户页的完整实现下面以仓库 page-controller 模块 的真实代码为例完整拆解该模式的每个组成部分。示例业务是用户在注册页/signup填写表单提交后重定向到用户页/user展示注册信息。4.1 模块结构与依赖模块源码位于 page-controller/src/main/java/com/iluwatar/page/controller共 7 个 Java 文件角色类职责应用入口AppSpring Boot 启动类页面控制器SignupController处理 /signup 的 GET 与 POST 请求数据模型SignupModel注册表单数据name/email/password页面视图SignupView决定注册页的展示与重定向视图名页面控制器UserController处理 /user 的 GET 请求数据模型UserModel用户信息数据name/email页面视图UserView决定用户页的展示视图名对应的 Maven 依赖见 page-controller/pom.xml模块基于 Spring MVCspring-webmvc 6.2.5与 Spring Boot Web Starterspring-boot-starter-web并引入 spring-boot-starter-thymeleaf 作为视图模板引擎测试侧使用 JUnit Jupiter、Mockito 与 spring-boot-starter-test。4.2 注册页控制器 SignupControllerSignupController.java 是注册页的页面控制器它通过Controller声明为 Spring MVC 控制器同时用Component纳入 Spring 容器管理Slf4j Controller Component public class SignupController { SignupView view new SignupView(); /** Signup Controller can handle http request and decide which model and view use. */ SignupController() {} /** Handle http GET request. */ GetMapping(/signup) public String getSignup() { return view.display(); } /** Handle http POST request and access model and view. */ PostMapping(/signup) public String create(SignupModel form, RedirectAttributes redirectAttributes) { LOGGER.info(form.getName()); LOGGER.info(form.getEmail()); redirectAttributes.addAttribute(name, form.getName()); redirectAttributes.addAttribute(email, form.getEmail()); redirectAttributes.addFlashAttribute(userInfo, form); return view.redirect(form); } }要点解析GET /signupgetSignup()直接委托视图的display()返回注册页视图名用于首次加载注册表单POST /signupcreate(SignupModel form, ...)接收表单自动绑定后的SignupModel通过RedirectAttributes携带数据后交给视图做重定向控制器内部持有SignupView实例模型与视图的选择完全由控制器决定这正是 Page Controller 与 MVC 结合的典型形态Slf4jLombok提供LOGGER字段便于观察请求处理过程。4.3 注册页模型与视图SignupModel / SignupView模型 SignupModel.java 使用 Lombok 的Data自动生成 getter/setter并用Component交给 Spring 管理Spring MVC 可据此完成表单参数到对象的自动绑定Component Data NoArgsConstructor public class SignupModel { private String name; private String email; private String password; }视图 SignupView.java 不关心 HTTP 细节只负责返回“视图名”和“重定向目标”Slf4j NoArgsConstructor public class SignupView { public String display() { LOGGER.info(display signup front page); return /signup; } /** redirect to user page. */ public String redirect(SignupModel form) { LOGGER.info( Redirect to user page with name form.getName() email form.getEmail()); return redirect:/user; } }这里有两个值得注意的工程细节display()返回的字符串/signup是视图名Spring MVC 会结合 ViewResolverpom 中引入的 Thymeleaf starter解析为具体模板redirect()返回redirect:/user前缀字符串Spring MVC 识别后发起302 重定向浏览器随后自动请求 /user 页面——这就是“注册成功后跳转用户页”的实现落点。4.4 用户页控制器 UserControllerUserController.java 是用户页的页面控制器只处理 GET 请求Slf4j Controller NoArgsConstructor public class UserController { private final UserView view new UserView(); /** Handle http GET request and access view and model. */ GetMapping(/user) public String getUserPath(SignupModel form, Model model) { model.addAttribute(name, form.getName()); model.addAttribute(email, form.getEmail()); return view.display(form); } }要点解析getUserPath(SignupModel form, Model model)中form由 Spring 依据 URL 查询参数自动绑定注册页重定向时携带的name、email即在此处被回填控制器把数据写入Model再委托UserView.display(form)返回视图名完成“控制器 → 模型 → 视图”的标准调用链。4.5 用户页模型与视图UserModel / UserView模型 UserModel.java 定义用户信息字段Data NoArgsConstructor public class UserModel { private String name; private String email; }视图 UserView.java 负责生成用户页展示指令Slf4j public class UserView { /** * displaying command to generate html. * * param user model content. */ public String display(SignupModel user) { LOGGER.info(display user html name user.getName() email user.getEmail()); return /user; } }从源码结构看UserController当前直接复用SignupModel作为请求绑定对象注册页重定向参数恰好能回填其中的 name/email 字段而UserModel作为用户页独立模型被定义——这恰好印证了模式要点具体使用哪个模型由控制器按需决定模型与页面之间不存在硬编码耦合。4.6 Spring Boot 应用入口 AppApp.java 是标准 Spring Boot 启动类Slf4j SpringBootApplication public class App { /** * Program entry point. * * param args command line args */ public static void main(final String[] args) { var context SpringApplication.run(App.class, args); if (args.length 0 test.equals(args[0])) { // Close the context immediately during tests to prevent Tomcat/background threads from // hanging the JVM context.close(); } } }运行方式在仓库根目录通过 Maven 启动该模块即可例如./mvnw -pl page-controller spring-boot:run启动后访问http://localhost:8080/signup可看到注册页提交表单后会被重定向到/user。需要说明的是main方法支持传入test参数以在测试场景下立即关闭上下文避免内嵌 Tomcat 线程挂住 JVM。5. 请求生命周期从 GET /signup 到 redirect:/user结合以上代码一次完整的注册流程可以串成如下调用链浏览器发起GET /signup→ 由SignupController.getSignup()处理 → 委托SignupView.display()→ 返回视图名/signupSpring MVC 渲染注册表单页用户填写表单并提交POST /signup→SignupController.create(SignupModel form, RedirectAttributes)接收表单绑定数据写入 name/email 的普通属性和userInfo的 Flash 属性 → 委托SignupView.redirect(form)→ 返回redirect:/user触发 302 重定向且 name/email 以查询参数形式追加到重定向 URL浏览器跟随重定向发起GET /user?name...email...→UserController.getUserPath(SignupModel form, Model model)自动绑定查询参数 → 将 name/email 写入Model→ 委托UserView.display(form)→ 返回视图名/user渲染用户信息页。这一过程中每个页面的请求处理都被封装在其对应的控制器中控制器只负责“接请求、选模型、派视图”业务细节由模型与视图各自承担——职责边界一目了然。6. UML 类图解析仓库 page-controller/etc/page-controller.urm.puml 源文件绘制了该模块的类图渲染图见 page-controller.urm.png类图所属包com.iluwatar.page.controller展示了 6 个类及其依赖关系App应用入口含main(args: String[])方法SignupController持有view: SignupView提供getSignup()与create(form, redirectAttributes)方法依赖SignupModel-model与SignupView-viewSignupModelname/email/password 三个字段及对应 getter/setterSignupViewdisplay()与redirect(form)方法UserController持有view: UserView提供getUserPath(form: SignupModel, model: Model)方法依赖UserModel-model与UserView-viewUserModelname/email 字段及 getter/setterUserViewdisplay(user: SignupModel)方法。从类图可以直观看到两个页面各自拥有独立的 Controller / Model / View 三元组控制器通过组合composition持有视图通过参数依赖模型页面之间唯一的连接点是“重定向 URL”彼此完全解耦。7. 适用场景Applicability根据原文档在以下情况应使用 Page Controller 模式实现一个站点其中大部分控制器逻辑都比较简单例如表单提交、页面跳转、简单查询展示等没有必要引入过重的集中式框架实现一个站点其中特定动作由某个具体的服务端页面来管理即“一个页面负责一类动作”页面与逻辑呈一一对应关系。简言之当你的站点页面数量可控、每个页面的请求处理逻辑清晰且相对独立时Page Controller 是性价比最高的选择。8. 何时使用与真实框架应用除本项目示例外Page Controller 模式在业界有着广泛的真实应用以下框架/平台天然采用这一思路Spring MVCJavaControllerGetMapping/PostMapping即页面控制器模式的标准实现本仓库示例即基于此Apache StrutsAction 与页面一一对应的经典框架JSFJavaServer Faces托管 Bean 配合页面视图同样体现一页一控制器的组织方式。选择该模式的典型时机包括开发一个每个页面或动作都需要特定处理的 Web 应用希望把请求处理逻辑与视图渲染逻辑分离需要在控制器、视图等不同层之间保持清晰关注点分离的场景。9. 优势与权衡优势Benefits关注点分离Separation of Concerns控制器逻辑与视图清晰分离应用更易管理与维护可复用性Reusability通用逻辑如日志、参数封装可在多个控制器间复用减少代码重复可测试性Testability控制器可以脱离视图独立测试提升单元测试覆盖率——这一点在第 11 节有测试代码直接佐证。权衡Trade-offs复杂度Complexity会为应用结构增加一定复杂度需要精心组织与文档化额外开销Overhead多了一层抽象与处理可能引入轻微性能开销。相比集中式的 Front ControllerPage Controller 牺牲了“统一入口”的集中管控能力换来了更直接的页面—逻辑映射与更低的框架侵入性适合中小规模、逻辑分散的站点。10. 与相关模式的协作Front Controller前端控制器常与 Page Controller 配合使用由 Front Controller 统一处理认证、日志等公共预处理逻辑再分发到各个 Page ControllerView Helper视图助手与 Page Controller 协同工作负责在视图层准备展示数据、处理格式化等表现逻辑Model-View-ControllerMVCPage Controller 是 MVC 架构的基础组成部分天然充当其中的 Controller 角色。在 java-design-patterns 仓库中上述模式均有独立模块如 front-controller、model-view-controller可以对照阅读理解它们与 Page Controller 的协作关系。11. 测试验证模式的可测试性Page Controller 的“控制器与视图解耦”特性让测试可以分别从单元测试与集成测试两个层面展开仓库测试目录 page-controller/src/test/java/com/iluwatar/page/controller 给出了两个代表性用例1SignupControllerTest —— 控制器单元测试SignupControllerTest.java 直接实例化控制器并调用方法验证注册流程的返回值Test void testSignup() { var controller new SignupController(); controller.getSignup(); RedirectAttributes redirectAttributes new RedirectAttributesModelMap(); String redirectPath controller.create(retrieveSignupData(), redirectAttributes); assertEquals(redirect:/user, redirectPath); }该测试用RedirectAttributesModelMap构造重定向属性断言create()返回的重定向路径为redirect:/user——不需要启动 Web 容器控制器即可被独立验证这正是可测试性优势的直接体现。2UserControllerTest —— 端到端集成测试UserControllerTest.java 使用 Spring Boot 的 MockMvc 模拟 HTTP 请求验证视图与模型的正确分发ExtendWith(SpringExtension.class) SpringBootTest AutoConfigureMockMvc public class UserControllerTest { Autowired MockMvc mockMvc; Test void testGetUserPath() throws Exception { this.mockMvc .perform(get(/user).param(name, Lily).param(email, Lilyemail.com)) .andExpect(status().isOk()) .andExpect(model().attribute(name, Lily)) .andExpect(model().attribute(email, Lilyemail.com)) .andReturn(); } }该测试验证了GET /user返回 200 状态且Model中正确写入name与email属性——证明控制器对“查询参数 → 模型 → 视图”的整条链路分发无误。此外模块还包含 AppTest.java、SignupModelTest.java 与 UserModelTest.java共同覆盖入口与模型层。12. 参考与致谢Page Controller 模式最早由 Martin Fowler 在《Patterns of Enterprise Application Architecture》企业应用架构模式中系统阐述其模式目录条目与书籍是该主题的权威参考来源。java-design-patterns 仓库的 page-controller 模块 与西语翻译文档 localization/es/page-controller/README.md 均以该模式为原型给出了可运行的 Java/Spring 实现读者可对照仓库源码与本文逐步验证。小结Page Controller 用最朴素的方式解决了 Web 应用最普遍的问题——让每个页面拥有专属的请求处理中枢。理解它的结构、边界与适用条件是掌握 MVC 分层、乃至后续理解 Front Controller 等更复杂表现层模式的基石。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐WinUI 与 .NET 版本校验实战指南SDK 双发布周期下的 WinUI 3 应用验证流程WinUI 与 .NET 版本校验实战指南SDK 双发布周期下的 WinUI 3 应用验证流程 本篇指南围绕 docs/netvalidationforwin示例工程教程Java 设计模式Front Controller 前端控制器模式深度解析基于 java-design-patterns 仓库Java 设计模式Front Controller 前端控制器模式深度解析基于 java design patterns 仓库 前端控制器Front C示例工程教程java-design-patterns实战宝典Spring集成指南java design patterns实战宝典Spring集成指南 引言设计模式与Spring的完美融合 你是否曾经在Spring项目中纠结于如何优雅地实示例工程教程上一篇从2.x到3.x的无缝迁移iView组件升级避坑指南下一篇Heroicons无障碍访问实践提升图标可访问性的5个实用技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考