设计模式:Spring MVC 中命令模式的核心映射与设计逻辑

目录

[1. 命令模式核心角色与 Spring MVC 组件的精准映射](#1. 命令模式核心角色与 Spring MVC 组件的精准映射)

[2. 核心执行流程(命令模式视角)](#2. 核心执行流程(命令模式视角))

[二、Spring MVC 命令模式的源码级实现拆解](#二、Spring MVC 命令模式的源码级实现拆解)

[步骤 1:抽象命令的定义(Spring 内置)](#步骤 1:抽象命令的定义(Spring 内置))

[步骤 2:具体命令的实现(开发者编写)](#步骤 2:具体命令的实现(开发者编写))

[步骤 3:调用者的实现(DispatcherServlet 核心逻辑)](#步骤 3:调用者的实现(DispatcherServlet 核心逻辑))

[步骤 4:命令参数解析(参数注入)](#步骤 4:命令参数解析(参数注入))

[步骤 5:命令执行结果处理](#步骤 5:命令执行结果处理)

[三、Spring MVC 命令模式的扩展实现(实战场景)](#三、Spring MVC 命令模式的扩展实现(实战场景))

[场景 1:命令的异步执行(异步命令)](#场景 1:命令的异步执行(异步命令))

[场景 2:命令的拦截增强(责任链 + 命令模式)](#场景 2:命令的拦截增强(责任链 + 命令模式))

[场景 3:命令的异常处理(统一结果)](#场景 3:命令的异常处理(统一结果))

[场景 4:RESTful 命令(标准化命令)](#场景 4:RESTful 命令(标准化命令))

[四、Spring MVC 命令模式的核心价值与设计优势](#四、Spring MVC 命令模式的核心价值与设计优势)

[1. 彻底解耦](#1. 彻底解耦)

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

[3. 标准化与规范化](#3. 标准化与规范化)

[4. 支持高级特性](#4. 支持高级特性)

五、总结


Spring MVC 是命令模式在 Web 框架中的经典落地实现,其核心是将 "HTTP 请求处理" 封装为独立的 "命令对象",由前端控制器(调用者)统一触发执行,业务层(接收者)完成实际逻辑。

1. 命令模式核心角色与 Spring MVC 组件的精准映射
命令模式角色 Spring MVC 对应组件 核心职责
抽象命令(Command) Handler 体系(Controller 接口、HandlerMethod 定义请求处理的统一接口(如 handleRequestinvoke),是所有请求处理命令的抽象规范
具体命令(ConcreteCommand) 自定义 @Controller 类 + 接口方法(如 UserController#getUser 实现抽象命令逻辑,封装具体的请求处理行为(参数解析、业务调用、响应返回)
调用者(Invoker) DispatcherServlet(前端控制器) 持有具体命令对象,触发命令执行(无需关心命令的具体处理逻辑)
接收者(Receiver) Service/DAO 层(如 UserServiceUserMapper 真正执行业务逻辑(查询数据、操作数据库),是命令的最终执行者
命令参数(Param) HandlerMethodArgumentResolver 解析的参数(@RequestParam/@RequestBody 命令执行所需的参数,由 Spring MVC 自动解析并注入命令对象
2. 核心执行流程(命令模式视角)

plaintext

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

二、Spring MVC 命令模式的源码级实现拆解

步骤 1:抽象命令的定义(Spring 内置)

Spring MVC 定义了多层抽象命令接口,最核心的是 ControllerHandlerMethod

java

运行

复制代码
// 基础抽象命令:Controller 接口(Spring 内置)
public interface Controller {
    /**
     * 命令执行方法:处理 HTTP 请求并返回视图模型
     */
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

// 注解式控制器的抽象命令:HandlerMethod(封装 @Controller 方法)
public class HandlerMethod {
    private final Object bean; // 具体命令对象(Controller 实例)
    private final Method method; // 具体命令方法(如 getUser)
    private final MethodParameter[] parameters; // 命令参数

    // 执行命令(核心方法)
    public Object invoke(Object... args) throws Exception {
        return method.invoke(bean, args);
    }
}

步骤 2:具体命令的实现(开发者编写)

开发者自定义的 @Controller 是命令模式的 "具体命令",每个接口方法对应一个独立的命令逻辑:

java

运行

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

    /**
     * 具体命令方法:查询用户(GET /user/{id})
     */
    @GetMapping("/{id}")
    public Result<User> getUser(
            @PathVariable Long id, // 命令参数1
            @RequestHeader String token // 命令参数2
    ) {
        // 命令执行逻辑:调用接收者完成业务
        User user = userService.getUserById(id);
        return Result.success(user);
    }

    /**
     * 具体命令方法:创建用户(POST /user)
     */
    @PostMapping
    public Result<Long> createUser(@RequestBody UserCreateDTO dto) {
        Long userId = userService.createUser(dto);
        return Result.success(userId);
    }
}

// 接收者:业务逻辑实现
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

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

    // 接收者核心方法:真正执行创建逻辑
    public Long createUser(UserCreateDTO dto) {
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setDeptId(dto.getDeptId());
        userMapper.insert(user);
        return user.getId();
    }
}

步骤 3:调用者的实现(DispatcherServlet 核心逻辑)

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

java

运行

复制代码
public class DispatcherServlet extends FrameworkServlet {
    // HandlerMapping:匹配命令对象(Controller)
    @Nullable
    private List<HandlerMapping> handlerMappings;
    // HandlerAdapter:适配并执行命令
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

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

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

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

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

            // 3. 执行命令前置拦截(责任链扩展)
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 4. 触发命令执行:调用 Controller 方法(核心步骤)
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 5. 执行命令后置处理
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        }
        // 6. 处理命令执行结果(渲染视图/返回响应)
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }

    // 匹配命令对象
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
}

步骤 4:命令参数解析(参数注入)

Spring MVC 通过 HandlerMethodArgumentResolver 解析命令参数,保证命令执行所需的参数正确注入:

java

运行

复制代码
// 自定义参数解析器(扩展命令参数类型)
@Component
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {
    /**
     * 判断是否支持该参数类型
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(Long.class) 
                && parameter.hasParameterAnnotation(CurrentUserId.class);
    }

    /**
     * 解析参数(从 Token 中获取当前用户ID,注入命令方法)
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String token = webRequest.getHeader("token");
        // 模拟从 Token 解析用户ID
        return JwtUtil.getUserIdFromToken(token);
    }
}

// 自定义注解:标记当前用户ID参数
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUserId {
}

// 命令方法使用自定义参数
@GetMapping("/current")
public Result<User> getCurrentUser(@CurrentUserId Long userId) {
    User user = userService.getUserById(userId);
    return Result.success(user);
}

步骤 5:命令执行结果处理

HandlerAdapter 执行命令后,DispatcherServlet 通过 ViewResolver 处理结果,返回响应:

java

运行

复制代码
// 命令执行结果处理(简化版)
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    // 异常处理(命令执行失败)
    if (exception != null) {
        mv = processHandlerException(request, response, mappedHandler, exception);
    }

    // 渲染视图(命令执行成功)
    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
}

三、Spring MVC 命令模式的扩展实现(实战场景)

场景 1:命令的异步执行(异步命令)

Spring MVC 支持 @Async 实现命令的异步执行,调用者触发后无需等待结果:

java

运行

复制代码
/**
 * 异步命令:异步查询用户数据
 */
@RestController
@RequestMapping("/async/user")
public class AsyncUserController {
    @Autowired
    private UserService userService;

    /**
     * 异步命令方法
     */
    @GetMapping("/{id}")
    @Async // 标记为异步命令
    public CompletableFuture<Result<User>> asyncGetUser(@PathVariable Long id) {
        // 异步执行命令逻辑
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000); // 模拟耗时操作
                User user = userService.getUserById(id);
                return Result.success(user);
            } catch (InterruptedException e) {
                return Result.fail("查询失败");
            }
        });
    }
}

// 配置异步执行器(启用 @Async)
@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(20);
        executor.setThreadNamePrefix("async-command-");
        executor.initialize();
        return executor;
    }
}

场景 2:命令的拦截增强(责任链 + 命令模式)

通过 HandlerInterceptor 实现命令执行前后的拦截,扩展命令功能:

java

运行

复制代码
/**
 * 命令拦截器:记录命令执行日志
 */
@Component
public class CommandLogInterceptor implements HandlerInterceptor {
    /**
     * 命令执行前:记录请求信息
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            String commandName = handlerMethod.getBeanType().getSimpleName() + "#" + handlerMethod.getMethod().getName();
            System.out.println("[命令拦截] 开始执行命令:" + commandName);
            request.setAttribute("startTime", System.currentTimeMillis());
        }
        return true;
    }

    /**
     * 命令执行后:记录执行耗时
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            String commandName = handlerMethod.getBeanType().getSimpleName() + "#" + handlerMethod.getMethod().getName();
            long startTime = (long) request.getAttribute("startTime");
            long cost = System.currentTimeMillis() - startTime;
            System.out.println("[命令拦截] 命令执行完成:" + commandName + ",耗时:" + cost + "ms");
        }
    }
}

// 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private CommandLogInterceptor commandLogInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(commandLogInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/error");
    }
}

场景 3:命令的异常处理(统一结果)

通过 @RestControllerAdvice 统一处理命令执行中的异常,保证响应格式一致:

java

运行

复制代码
/**
 * 命令异常处理器:统一处理命令执行失败
 */
@RestControllerAdvice
public class CommandExceptionHandler {
    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        return Result.fail(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleParamException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.fail(400, "参数错误:" + message);
    }

    /**
     * 处理通用异常
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        e.printStackTrace();
        return Result.fail(500, "服务器内部错误");
    }
}

// 业务异常类
public class BusinessException extends RuntimeException {
    private int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    // getter/setter
}

// 统一响应结果
@Data
public class Result<T> {
    private int code;
    private String message;
    private T data;

    // 静态方法:成功/失败响应
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("成功");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> fail(int code, String message) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

场景 4:RESTful 命令(标准化命令)

RESTful 接口是命令模式的标准化实现,每个 HTTP 方法对应一个命令类型:

java

运行

复制代码
/**
 * RESTful 命令:用户 CRUD
 */
@RestController
@RequestMapping("/api/users")
public class UserRestController {
    @Autowired
    private UserService userService;

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

    // GET 命令:查询用户列表
    @GetMapping
    public Result<Page<User>> list(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize
    ) {
        Page<User> page = userService.listPage(new Page<>(pageNum, pageSize));
        return Result.success(page);
    }

    // POST 命令:创建用户
    @PostMapping
    public Result<Long> create(@RequestBody @Valid UserCreateDTO dto) {
        return Result.success(userService.createUser(dto));
    }

    // PUT 命令:更新用户
    @PutMapping("/{id}")
    public Result<Boolean> update(@PathVariable Long id, @RequestBody UserUpdateDTO dto) {
        return Result.success(userService.updateUser(id, dto));
    }

    // DELETE 命令:删除用户
    @DeleteMapping("/{id}")
    public Result<Boolean> delete(@PathVariable Long id) {
        return Result.success(userService.deleteUser(id));
    }
}

四、Spring MVC 命令模式的核心价值与设计优势

1. 彻底解耦

  • 调用者与命令解耦DispatcherServlet 无需知道 UserController 的存在,只需通过 HandlerMapping 匹配命令;
  • 命令与接收者解耦UserController 只需调用 UserService 的方法,无需关心 UserService 的具体实现;
  • 命令与参数解耦 :参数解析由 HandlerMethodArgumentResolver 统一处理,命令方法只需声明参数类型。

2. 高度可扩展

  • 新增命令 :只需新增 Controller 类 / 方法,无需修改 DispatcherServlet 核心逻辑;
  • 扩展参数类型 :只需实现 HandlerMethodArgumentResolver,支持自定义参数注解;
  • 扩展命令执行逻辑 :通过 HandlerInterceptor 拦截命令执行,添加日志、权限、限流等功能。

3. 标准化与规范化

  • 所有请求都遵循 "调用者 → 命令 → 接收者" 的统一流程,便于维护和排查问题;
  • RESTful 命令标准化了 HTTP 方法与业务操作的映射,降低团队协作成本。

4. 支持高级特性

  • 异步命令 :通过 @Async 实现非阻塞执行,提升系统吞吐量;
  • 命令重试:结合 Spring Retry 实现命令执行失败后的重试;
  • 命令限流:结合 Sentinel/Gateway 实现命令级别的限流控制。

五、总结

Spring MVC 是命令模式在 Web 框架中的极致落地,核心要点可总结为:

  1. 核心映射DispatcherServlet 是调用者,@Controller 方法是具体命令,Service 是接收者;
  2. 核心流程:调用者匹配命令 → 解析命令参数 → 执行命令 → 处理命令结果 → 返回响应;
  3. 扩展能力:通过拦截器、参数解析器、异常处理器扩展命令的执行逻辑;
  4. 设计优势:解耦请求触发与业务执行,支持标准化、异步化、可扩展的请求处理。

理解 Spring MVC 中的命令模式,不仅能帮助你更深入地掌握框架原理,还能指导你设计高内聚、低耦合的业务代码(如将复杂业务操作封装为 "命令对象",实现可复用、可追踪的业务逻辑)。

相关推荐
BD_Marathon2 小时前
Spring是什么
java·后端·spring
.简.简.单.单.2 小时前
Design Patterns In Modern C++ 中文版翻译 第八章 组合
java·c++·设计模式
七夜zippoe2 小时前
Spring MVC请求处理流程源码分析与DispatcherServlet核心逻辑
java·spring·mvc·过滤器·拦截器
老朱佩琪!2 小时前
Unity责任链模式
unity·设计模式·责任链模式
.简.简.单.单.3 小时前
Design Patterns In Modern C++ 中文版翻译 第九章 装饰器
开发语言·c++·设计模式
小生不才yz3 小时前
行为型模式 - 状态模式
设计模式
廋到被风吹走3 小时前
【Spring】Spring Core解析
java·spring·rpc
悟能不能悟3 小时前
Spring HATEOAS 详细介绍
java·后端·spring
海南java第二人3 小时前
Java类加载机制深度解析:从双亲委派到自定义加载的完整指南
java·spring