从Servlet 到SpringMVC

Servlet

Servlet 是 java Web的基石,它的主要作用是处理Web请求并生成响应。在早期java web开发阶段开发人员在开发 web 接口时需要针对不同的业务实现不同的 Servlet 类,参数解析、类型转换需手动实现,并且要分别配置对应的 url 地址映射。

下面以一个简单的 Servlet 接口用例演示开发过程,该接口要求通过入参中的用户ID查询用户信息并返回

java 复制代码
public class UserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        // 1. 从请求参数中获取 userId
        String userId = request.getParameter("userId");
        // 2. 如果未传递参数,提示异常
        if (userId == null || userId.trim().isEmpty()) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "userId is required");
            return;
        }
        // 3.查询用户信息,并转换为json 字符串
        UserInfo = queryUserInfo(userId);
        // 4. 将用户信息转换为 JSON 字符串
        String userInfoJson = JsonUtil.toJson(UserInfo);
                
        // 5. 向客户端输出响应内容
        response.setContentType("application/json; charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            String json = "{\"data\": "+userInfoJson+"}";
            out.println(json);
        }
    }
}

在这段代码中针对开发人员视角只需要考虑如何查询用户信息等业务处理,而在上面的代码中却不得不处理参数解析、异常处理、视图渲染等各种问题,并且在不同的servlet中这部分代码都需要重复出现,代码冗余繁琐。

由此可以看出常规的Servlet 开发存在重复劳动、耦合性差等缺点,为了解决这些问题、需要将这部分与业务无关的代码进行抽取解耦,实现职责分离,标准化处理流程。

一个简单的 Servlet MVC 框架

首先我们需要定义一个Servlet 并将url-pattern请求地址映射为/ ,这样所有接口请求都会交由该Servlet 统一处理,该 Servlet 解析请求地址然后转发给对应的请求处理类RequestHandle,并将参数解析、异常处理、响应结果序列化等功能统一处理。

java 复制代码
public class DispatchServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        try {
            // 1.获取处理Handle
            HandlerMethod handle = getHandler(request.getRequestURI());

            // 2.参数解析
            Object[] args = resolveParams(handle.getMethod(), request);

            // 3.执行业务方法
            Object invoke = invokeInner(handle.getBean(), handle.getMethod(), args);

            // 4.处理返回值
            handleReturnValue(invoke, response);
        }catch (Exception e) {
            // 5.统一异常处理
            handleException(e, response);
        }
    }

而为了标识请求处理类RequestHandle,定义@Controller注解并添加在处理类上。针对不同的地址映射还需要定义@RequestMapping(value='/user/userInfo') 注解,并指定映射的请求地址路径 ,为了获取请求中的指定参数需要定义 @RequestParam(value="userId")注解。

java 复制代码
@Controller
@RequestMapping(value = "/user")
public class UserRequestHandle {

    @RequestMapping(value = "/userInfo")
    public UserInfo handle(@RequestParam(value="userId") String userId) {
        if (userId == null || userId.trim().isEmpty()) {
            throw new BusinessException("userId is required");
        }
        // 查询用户信息
        UserInfo userInfo= queryUserInfo(userId);
        if(userInfo==null){
            // 抛出业务异常
            throw new BusinessException("用户不存在");
        }
        return userInfo;
    }
}

后续再开发新的接口只需要实现RequestHandle实例,专注于业务处理,实现职责分离,业务解耦。

而到这里为止,一个简单的MVC框架已经初见雏形,而熟悉的朋友已经猜到,SpringMVC其实采用了类似的实现,但它更加成熟稳定,也是被广泛采用的web框架。了解了一个MVC框架的大致原理,可以更加清晰明了的了解SpringMVC是如何实现请求的处理与响应的。

Spring MVC

Spring MVC 是 Spring 框架中用于构建 Web 应用的模块,基于经典的 MVC 设计模式(Model-View-Controller),提供了一套灵活、高效的 Web 开发框架。

SpringMVC 框架内部实现了各种核心组件以满足不同的处理逻辑,下面先简单介绍下这些组件:

  • DispatchServlet:作为整个框架的入口,接收所有 HTTP 请求,并协调其他组件完成请求处理。
  • HandleMapping:根据请求的 URL 找到对应的处理器(Handler),常见实现如下
    • RequestMappingHandlerMapping:处理 @RequestMapping 注解的控制器
    • BeanNameUrlHandlerMapping:根据 Bean 名称匹配 URL
    • SimpleUrlHandlerMapping:显式配置 URL 到处理器的映射。
  • HandlerAdapter: 适配不同类型的处理器(如 Controller、Servlet 或其他 POJO),统一调用处理逻辑。常见实现如下
    • RequestMappingHandlerAdapter:处理 @Controller@RequestMapping 注解的处理器
    • SimpleControllerHandlerAdapter:适配实现了 Controller 接口的处理器。
    • HttpRequestHandlerAdapter:适配 HttpRequestHandler 接口的处理器
  • HandlerMethodArgumentResolver:解析处理器方法参数,并将请求中的信息绑定到控制器方法的参数上,常见实现如下
    • RequestParamMethodArgumentResolver: 解析@RequestParam注解获取 URL 或表单参数
    • RequestResponseBodyMethodProcessor:解析@RequestBody将请求Json转换为参数
    • PathVariableMethodArgumentResolver:解析@PathVariable获取URL路径参数
  • HandlerMethodReturnValueHandler:处理控制器方法返回值,可支持自定义,比如响应加密,统一结果响应等。常见实现如下:
    • RequestResponseBodyMethodProcessor:解析@ResponseBody注解,将返回值转换为Json
  • HandlerExceptionResolver:处理控制器抛出的异常,返回统一的错误页面或 JSON 响应。常见实现如下
    • ExceptionHandlerExceptionResolver:处理 @ExceptionHandler 注解的方法。
    • SimpleMappingExceptionResolver:映射异常类型到错误视图。
  • HandlerInterceptor:处理器拦截器,在请求处理前后添加特定逻辑,比如日志打印,耗时统计等。

各个组件接口在 SpringMVC框架中扮演着不同的角色,一个普通接口请求处理的整个流程如下图:

Spring MVC 的组件化设计通过 接口与实现分离,提供了高度的扩展性,开发着可以自定义实现不同的处理实现,比如自定义参数解析或者响应序列化方式等。

相关推荐
GoGeekBaird2 小时前
69天探索操作系统-第66天:为现代操作系统设计高级实时进程间通信机制
后端·操作系统
还是鼠鼠2 小时前
单元测试-概述&入门
java·开发语言·后端·单元测试·log4j·maven
我最厉害。,。4 小时前
接口安全&SOAP&OpenAPI&RESTful&分类特征导入&项目联动检测
后端·restful
AntBlack6 小时前
计算机视觉 : 端午无事 ,图像处理入门案例一文速通
后端·python·计算机视觉
福大大架构师每日一题7 小时前
2025-06-02:最小可整除数位乘积Ⅱ。用go语言,给定一个表示正整数的字符串 num 和一个整数 t。 定义:如果一个整数的每一位都不是 0,则称该整数为
后端
Code_Artist7 小时前
[Mybatis] 因 0 != null and 0 != '' 酿成的事故,害得我又过点啦!
java·后端·mybatis
程序员博博7 小时前
看到这种代码,我直接气到想打人
后端
南雨北斗8 小时前
php 图片压缩函数
后端
L2ncE8 小时前
ES101系列08 | 数据建模和索引重建
java·后端·elasticsearch
还是鼠鼠8 小时前
Maven---配置本地仓库
java·开发语言·后端·maven