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

相关推荐
z263730561110 分钟前
springboot继承使用mybatis-plus举例相关配置,包括分页插件以及封装分页类
spring boot·后端·mybatis
追逐时光者3 小时前
分享一个纯净无广、原版操作系统、开发人员工具、服务器等资源免费下载的网站
后端·github
JavaPub-rodert4 小时前
golang 的 goroutine 和 channel
开发语言·后端·golang
ivygeek6 小时前
MCP:基于 Spring AI Mcp 实现 webmvc/webflux sse Mcp Server
spring boot·后端·mcp
GoGeekBaird6 小时前
69天探索操作系统-第54天:嵌入式操作系统内核设计 - 最小内核实现
后端·操作系统
鱼樱前端7 小时前
Java Jdbc相关知识点汇总
java·后端
小嘚7 小时前
springCloud的学习
学习·spring·spring cloud
canonical_entropy7 小时前
NopReport示例-动态Sheet和动态列
java·后端·excel
kkk哥7 小时前
基于springboot的母婴商城系统(018)
java·spring boot·后端
张小娟8 小时前
springCloud集成tdengine(原生和mapper方式) 其二 原生篇
spring·spring cloud·tdengine