设计模式:命令模式(Spring MVC中的实践)

目录

[一、Spring MVC 中命令模式的核心映射关系](#一、Spring MVC 中命令模式的核心映射关系)

[二、Spring MVC 中命令模式的执行流程](#二、Spring MVC 中命令模式的执行流程)

关键环节拆解(结合源码级逻辑)

[1. 抽象命令:Handler 接口体系](#1. 抽象命令:Handler 接口体系)

[2. 具体命令:自定义 Controller(封装请求处理逻辑)](#2. 具体命令:自定义 Controller(封装请求处理逻辑))

[3. 调用者:DispatcherServlet(触发命令执行)](#3. 调用者:DispatcherServlet(触发命令执行))

[4. 接收者:Service/DAO 层(执行核心业务)](#4. 接收者:Service/DAO 层(执行核心业务))

[三、Spring MVC 中命令模式的扩展特性(命令模式的高级用法)](#三、Spring MVC 中命令模式的扩展特性(命令模式的高级用法))

[1. 命令参数化(动态适配不同请求参数)](#1. 命令参数化(动态适配不同请求参数))

[2. 命令的异常处理(统一命令执行结果)](#2. 命令的异常处理(统一命令执行结果))

[3. 命令拦截(责任链 + 命令模式结合)](#3. 命令拦截(责任链 + 命令模式结合))

[4. 命令的异步执行(异步命令)](#4. 命令的异步执行(异步命令))

[四、Spring MVC 中命令模式的典型应用场景](#四、Spring MVC 中命令模式的典型应用场景)

[1. 不同请求类型的命令适配](#1. 不同请求类型的命令适配)

[2. RESTful 接口(REST 命令)](#2. RESTful 接口(REST 命令))

[3. 表单提交(命令对象模式)](#3. 表单提交(命令对象模式))

[五、Spring MVC 命令模式的核心价值](#五、Spring MVC 命令模式的核心价值)

[1. 解耦请求触发与业务执行](#1. 解耦请求触发与业务执行)

[2. 高度可扩展](#2. 高度可扩展)

[3. 统一请求处理流程](#3. 统一请求处理流程)

[六、命令模式 vs Spring MVC 传统理解的区别](#六、命令模式 vs Spring MVC 传统理解的区别)

总结


一、Spring MVC 中命令模式的核心映射关系

Spring MVC 的核心请求处理流程完全贴合命令模式的设计思想,其本质是将 "HTTP 请求处理" 封装为命令对象,由前端控制器(调用者)触发执行,业务逻辑层(接收者)完成实际处理。

先明确命令模式核心角色与 Spring MVC 组件的对应关系:

命令模式角色 Spring MVC 对应组件 核心职责
抽象命令(Command) Handler 接口体系(ControllerHttpRequestHandlerServlet 定义请求处理的统一接口(如 handleRequestdoGet/doPost),是所有 "请求处理命令" 的抽象
具体命令(ConcreteCommand) 自定义 @Controller 类(如 UserController)、@RestController 实现抽象命令接口(隐式实现),封装具体的请求处理逻辑(如 /user/get 接口)
调用者(Invoker) DispatcherServlet(前端控制器) 持有具体命令对象(Handler),触发命令执行(调用 HandlerAdapter.handle()),不关心具体处理逻辑
接收者(Receiver) Service/DAO 层(如 UserServiceUserMapper 真正执行业务逻辑(如查询用户、操作数据库),是命令的最终执行者
命令参数(Command Param) HandlerMethodArgumentResolver 解析的参数(如 @RequestParam@RequestBody 命令对象执行所需的参数,由 Spring MVC 自动解析并传入具体命令

二、Spring MVC 中命令模式的执行流程

Spring MVC 处理 HTTP 请求的过程,就是命令模式 "调用者触发命令、命令调用接收者" 的完整落地,核心流程如下:

plaintext

复制代码
客户端发送 HTTP 请求 → 
  1. Tomcat 转发请求到 DispatcherServlet(调用者)→ 
  2. DispatcherServlet 通过 HandlerMapping 匹配到具体的 Controller(具体命令)→ 
  3. DispatcherServlet 委托 HandlerAdapter 执行 Controller 方法(触发命令执行)→ 
  4. Controller 调用 Service/DAO(接收者)完成业务逻辑 → 
  5. Service/DAO 返回结果给 Controller(命令执行完成)→ 
  6. DispatcherServlet 渲染视图/返回 JSON(命令执行结果响应)

关键环节拆解(结合源码级逻辑)

1. 抽象命令:Handler 接口体系

Spring MVC 定义了多种 "请求处理命令" 的抽象,最核心的是 Controller 接口(简化版):

java

运行

复制代码
// 抽象命令:处理 HTTP 请求的核心接口
public interface Controller {
    // 命令执行方法:处理请求并返回 ModelAndView
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

此外,@Controller 注解的类本质是 "隐式的具体命令",Spring MVC 通过 RequestMappingHandlerAdapter 适配其方法执行,等价于实现了抽象命令的 execute 方法。

2. 具体命令:自定义 Controller(封装请求处理逻辑)

开发者编写的 @Controller 是命令模式的 "具体命令",每个接口方法对应一个具体的命令逻辑:

java

运行

复制代码
// 具体命令:用户模块请求处理命令
@Controller
@RequestMapping("/user")
public class UserController { // 具体命令类
    // 接收者:业务逻辑层
    @Autowired
    private UserService userService;

    // 具体命令方法:处理 /user/get 请求
    @GetMapping("/get")
    @ResponseBody
    public User getUser(@RequestParam Long id) { // 命令参数
        // 命令执行逻辑:调用接收者完成业务
        return userService.getUserById(id);
    }
}
  • UserController 是 "具体命令对象",getUser 方法是命令的 "执行逻辑";
  • @RequestParam Long id 是命令执行所需的参数;
  • UserService 是命令的 "接收者",负责真正的业务处理。
3. 调用者:DispatcherServlet(触发命令执行)

DispatcherServlet 是命令模式的 "调用者",核心逻辑在 doDispatch 方法(简化版):

java

运行

复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;

    try {
        ModelAndView mv = null;
        // 1. 匹配具体命令:通过 HandlerMapping 找到对应的 Controller(具体命令)
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }

        // 2. 获取命令执行器:HandlerAdapter(适配不同类型的命令)
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 3. 触发命令执行:调用 Controller 方法(执行具体命令)
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        // 4. 处理命令执行结果:渲染视图/返回响应
        processDispatchResult(processedRequest, response, mappedHandler, mv, null);
    } catch (Exception ex) {
        // 异常处理
    }
}
  • getHandler():从 HandlerMapping(如 RequestMappingHandlerMapping)中匹配到 UserController 这个具体命令;
  • ha.handle():触发命令执行(调用 UserController.getUser()),调用者无需知道 getUser 的具体逻辑;
  • 整个过程中,DispatcherServlet 只负责 "触发命令",不关心命令的具体实现。
4. 接收者:Service/DAO 层(执行核心业务)

UserService 是命令的 "接收者",包含真正的业务逻辑,与命令对象解耦:

java

运行

复制代码
// 接收者:用户业务逻辑处理
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    // 接收者的核心方法:真正执行"查询用户"逻辑
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
}

命令对象(UserController)只需调用接收者的方法,无需关心 "如何查询数据库",实现了 "请求触发" 与 "业务执行" 的解耦。

三、Spring MVC 中命令模式的扩展特性(命令模式的高级用法)

Spring MVC 基于命令模式的核心设计,扩展了命令模式的典型能力,贴合实际业务需求:

1. 命令参数化(动态适配不同请求参数)

命令模式支持 "命令参数化",Spring MVC 通过 HandlerMethodArgumentResolver 实现参数的动态解析,让同一个命令(Controller 方法)适配不同参数:

java

运行

复制代码
// 同一个命令方法,支持不同参数(参数化命令)
@PostMapping("/create")
@ResponseBody
public Long createUser(@RequestBody User user) { // JSON 参数
    return userService.createUser(user);
}

@PostMapping("/update")
@ResponseBody
public Boolean updateUser(@RequestParam Long id, @RequestParam String name) { // 普通参数
    return userService.updateUserName(id, name);
}
  • 不同参数类型(JSON、表单、路径参数)通过不同的 ArgumentResolver 解析后传入命令方法;
  • 命令对象(Controller)无需关心参数来源,只需接收参数并调用接收者,体现命令模式 "参数封装" 的特性。

2. 命令的异常处理(统一命令执行结果)

Spring MVC 通过 HandlerExceptionResolver 统一处理命令执行中的异常,等价于命令模式中 "命令执行失败的统一处理":

java

运行

复制代码
// 全局异常处理器(统一处理命令执行异常)
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        return Result.fail(e.getCode(), e.getMessage());
    }
}
  • 命令执行(Controller 方法)中抛出的异常,由统一的处理器处理,避免每个命令都编写异常逻辑;
  • 符合命令模式 "解耦命令执行与异常处理" 的设计思想。

3. 命令拦截(责任链 + 命令模式结合)

Spring MVC 的 HandlerInterceptor 是责任链模式,但与命令模式结合,实现命令执行前后的拦截增强:

java

运行

复制代码
// 拦截命令执行前后的逻辑
@Component
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 命令执行前:记录请求日志
        System.out.println("命令执行前:" + request.getRequestURI());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 命令执行后:记录响应日志
        System.out.println("命令执行后:" + response.getStatus());
    }
}
  • 拦截器在命令(Controller 方法)执行前后增强逻辑,不修改命令本身,符合命令模式 "开闭原则";
  • 责任链 + 命令模式的结合,让命令执行流程更灵活。

4. 命令的异步执行(异步命令)

Spring MVC 支持 @Async 实现命令的异步执行,等价于命令模式的 "异步命令":

java

运行

复制代码
// 异步命令:后台执行,不阻塞请求响应
@GetMapping("/async/get")
@ResponseBody
@Async
public CompletableFuture<User> asyncGetUser(@RequestParam Long id) {
    return CompletableFuture.supplyAsync(() -> userService.getUserById(id));
}
  • 调用者(DispatcherServlet)触发命令后,无需等待命令执行完成,直接返回响应;
  • 命令在后台线程中执行,体现命令模式 "解耦调用者与命令执行时机" 的特性。

四、Spring MVC 中命令模式的典型应用场景

1. 不同请求类型的命令适配

Spring MVC 支持多种 "命令类型"(Handler),适配不同的请求处理场景,均基于命令模式:

命令类型 适用场景 示例
@Controller + @RequestMapping 普通 MVC 请求(返回视图 / JSON) UserController 处理 /user/* 请求
HttpRequestHandler 低级别 Servlet 风格请求处理 处理静态资源、文件下载
Servlet 原生 Servlet 适配 集成老旧 Servlet 程序
FunctionController 函数式请求处理(Spring 6+) RouterFunctions.route() 定义路由

2. RESTful 接口(REST 命令)

RESTful 接口是命令模式的典型落地,每个 HTTP 方法(GET/POST/PUT/DELETE)对应一个具体命令:

java

运行

复制代码
// RESTful 命令:CRUD 对应不同命令
@RestController
@RequestMapping("/api/user")
public class UserRestController {
    @Autowired
    private UserService userService;

    // GET 命令:查询用户
    @GetMapping("/{id}")
    public User get(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    // POST 命令:创建用户
    @PostMapping
    public Long create(@RequestBody User user) {
        return userService.createUser(user);
    }

    // PUT 命令:更新用户
    @PutMapping("/{id}")
    public Boolean update(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    // DELETE 命令:删除用户
    @DeleteMapping("/{id}")
    public Boolean delete(@PathVariable Long id) {
        return userService.deleteUser(id);
    }
}
  • 每个 REST 接口方法是一个独立的 "具体命令",封装了对应的业务操作;
  • 调用者(DispatcherServlet)根据 HTTP 方法和路径匹配对应的命令,触发执行。

3. 表单提交(命令对象模式)

Spring MVC 支持将表单参数封装为 "命令对象"(Command Object),是命令模式的直接应用:

java

运行

复制代码
// 命令对象:封装表单参数
@Data
public class UserForm {
    private String username;
    private String password;
    private Integer age;
}

// 具体命令:处理表单提交
@Controller
@RequestMapping("/form")
public class UserFormController {
    @Autowired
    private UserService userService;

    // 命令方法:接收命令对象参数
    @PostMapping("/user")
    public String submitUserForm(UserForm userForm) {
        userService.createUserFromForm(userForm);
        return "redirect:/user/list";
    }
}
  • UserForm 是 "命令参数对象",封装了表单提交的所有参数;
  • submitUserForm 是命令执行方法,接收参数并调用接收者完成业务。

五、Spring MVC 命令模式的核心价值

1. 解耦请求触发与业务执行

  • DispatcherServlet(调用者)只需触发命令执行,无需知道 "如何查询用户、如何创建订单";
  • Controller(命令对象)只需调用 Service(接收者),无需关心 "请求如何转发、参数如何解析";
  • Service(接收者)只需专注业务逻辑,无需关心 "请求从哪里来、响应如何返回"。

2. 高度可扩展

  • 新增业务接口(命令),只需新增 Controller 类 / 方法,无需修改 DispatcherServlet 核心逻辑;
  • 新增参数类型(如自定义注解参数),只需新增 ArgumentResolver,无需修改命令对象;
  • 新增命令类型(如 WebSocket 处理),只需实现对应的 Handler 接口,适配 HandlerAdapter 即可。

3. 统一请求处理流程

所有请求都通过 "调用者 → 命令对象 → 接收者" 的流程处理,保证了请求处理的一致性:

  • 统一的参数解析、异常处理、拦截增强;
  • 统一的请求日志、性能监控、安全校验。

六、命令模式 vs Spring MVC 传统理解的区别

很多开发者认为 Spring MVC 是 "MVC 模式",但 MVC 是架构模式,而命令模式是行为模式,二者并不冲突:

  • MVC 模式:关注 "模型(Model)、视图(View)、控制器(Controller)" 的分层,解决 "数据、展示、交互" 的分离;
  • 命令模式:关注 "请求触发与业务执行" 的解耦,解决 "谁触发、谁执行、怎么执行" 的分离;
  • Spring MVC 中,Controller 既是 MVC 模式的 "控制器",也是命令模式的 "具体命令对象",是两种模式的结合落地。

总结

Spring MVC 是命令模式在 Web 框架中的经典落地,核心要点:

  1. 核心映射DispatcherServlet 是调用者,@Controller 是具体命令,Service/DAO 是接收者;
  2. 核心流程:调用者匹配命令 → 触发命令执行 → 命令调用接收者 → 处理执行结果;
  3. 核心价值:解耦请求触发与业务执行,支持命令的参数化、异步化、拦截增强;
  4. 扩展特性:结合责任链(拦截器)、参数解析、异常处理,让命令模式适配 Web 场景的复杂需求。

理解 Spring MVC 中的命令模式,能帮助开发者更清晰地设计可扩展的 Controller 层,也能更好地理解 Spring MVC 的核心源码逻辑(如 DispatcherServlet.doDispatchHandlerAdapter.handle)。

相关推荐
明洞日记2 小时前
【设计模式手册021】代理模式 - 如何控制对象访问
设计模式·系统安全·代理模式
山沐与山2 小时前
【设计模式】Python策略模式:从入门到实战
python·设计模式·策略模式
阿拉斯攀登3 小时前
设计模式:责任链模式(mybatis数据权限实现)
设计模式·mybatis·责任链模式
syt_10133 小时前
设计模式之-模板模式
设计模式
阿拉斯攀登3 小时前
设计模式:责任链模式(MyBatis)
设计模式·mybatis·责任链模式
崎岖Qiu4 小时前
【设计模式笔记19】:建造者模式
java·笔记·设计模式·建造者模式
syt_10134 小时前
设计模式之-享元模式
javascript·设计模式·享元模式
想学后端的前端工程师18 小时前
【Java设计模式实战应用指南:23种设计模式详解】
java·开发语言·设计模式
Revol_C19 小时前
开箱即用!轻量级轮询方案,支持同步获取轮询结果!
前端·javascript·设计模式