news 2026/9/13 9:39:15

Servlet与MVC模式解析及Spring MVC演进

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Servlet与MVC模式解析及Spring MVC演进

1. Servlet与MVC模式解析

Servlet作为JavaWeb开发的核心技术,本质上是一个运行在服务器端的Java程序,用于处理客户端请求并生成动态响应。与传统的CGI技术相比,Servlet采用线程池处理请求,性能优势明显。在实际项目中,一个典型的Servlet类需要继承HttpServlet并重写doGet()或doPost()方法:

public class UserServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 业务逻辑处理 String username = request.getParameter("username"); UserService service = new UserService(); boolean result = service.register(username); // 响应处理 if(result) { request.setAttribute("message", "注册成功"); } else { request.setAttribute("message", "用户名已存在"); } request.getRequestDispatcher("/result.jsp").forward(request, response); } }

这种原始开发方式虽然直接,但随着业务复杂度上升会面临几个典型问题:

  1. 单个Servlet处理多个功能导致代码臃肿(如UserServlet同时处理登录、注册、修改密码)
  2. 业务逻辑与视图渲染强耦合,不利于分工协作
  3. 请求参数处理、数据验证等重复代码充斥在各个方法中

经验之谈:在实际项目中,建议每个Servlet只处理单一业务功能。例如LoginServlet只处理登录相关请求,RegisterServlet只处理注册请求。这样虽然类数量增加,但维护性大幅提升。

2. MVC架构的演进与实践

MVC(Model-View-Controller)模式通过职责分离解决了上述痛点。在JavaWeb中,其典型实现方式为:

  • Model(模型):业务逻辑与数据访问层,通常包含:

    • Service类(如UserService)处理核心业务逻辑
    • DAO类(如UserDao)负责数据库操作
    • POJO类(如User)作为数据载体
  • View(视图):展示层技术,传统采用JSP,现代项目更多使用Thymeleaf、FreeMarker等模板引擎:

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>操作结果</title> </head> <body> <h1>${message}</h1> <a href="index.jsp">返回首页</a> </body> </html>
  • Controller(控制器):接收请求并协调模型与视图,早期使用Servlet,现代框架中演化为@Controller注解类

实际项目中的调用流程通常为:

  1. 用户提交表单到Servlet(Controller)
  2. Servlet调用Service处理业务(Model)
  3. Service通过DAO操作数据库
  4. 结果通过setAttribute()存入request
  5. Servlet转发到JSP页面(View)
  6. JSP通过EL表达式${}展示数据

3. 从Servlet到Spring MVC的演进

虽然原生Servlet可以实现MVC,但存在配置繁琐、功能有限等问题。Spring MVC通过前端控制器模式进行了优化:

  1. DispatcherServlet:作为统一入口接收所有请求
  2. HandlerMapping:将请求映射到具体Controller
  3. Controller:使用@Controller注解的处理器类
  4. ModelAndView:封装模型数据和视图信息
  5. ViewResolver:解析逻辑视图名到具体视图技术

对比示例:

// 传统Servlet方式 public class OldServlet extends HttpServlet { protected void doGet(...) { User user = userService.getUser(id); request.setAttribute("user", user); request.getRequestDispatcher("/userDetail.jsp").forward(...); } } // Spring MVC方式 @Controller public class NewController { @GetMapping("/user/{id}") public String getUser(@PathVariable int id, Model model) { model.addAttribute("user", userService.getUser(id)); return "userDetail"; // 对应模板文件userDetail.html } }

避坑指南:在迁移旧项目时,建议逐步替换。可以先引入DispatcherServlet处理新功能,旧功能保持Servlet实现,通过URL映射区分新旧路由。

4. 实战:学生管理系统案例设计

基于Servlet和JSP实现MVC的典型项目结构如下:

/project /src /main /java /com.example.controller # Servlet控制器 /com.example.service # 业务逻辑层 /com.example.dao # 数据访问层 /com.example.model # 实体类 /webapp /WEB-INF web.xml # 部署描述符 /views # JSP视图文件 /static /css # 静态资源 /js /target # 构建输出目录

关键配置示例(web.xml):

<servlet> <servlet-name>studentServlet</servlet-name> <servlet-class>com.example.controller.StudentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>studentServlet</servlet-name> <url-pattern>/student/*</url-pattern> </servlet-mapping>

分页查询的实现要点:

  1. DAO层使用LIMIT语句:
public List<Student> findByPage(int pageNo, int pageSize) { String sql = "SELECT * FROM students LIMIT ?,?"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class), (pageNo-1)*pageSize, pageSize); }
  1. Service层计算分页参数:
public PageInfo<Student> getPage(int pageNo, int pageSize) { int total = studentDao.count(); List<Student> list = studentDao.findByPage(pageNo, pageSize); return new PageInfo<>(pageNo, pageSize, total, list); }
  1. JSP页面显示分页导航:
<c:forEach begin="1" end="${pageInfo.totalPage}" var="i"> <a href="student?action=list&pageNo=${i}">${i}</a> </c:forEach>

5. 常见问题排查与性能优化

典型问题1:中文乱码解决方案:

// Servlet中设置编码 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // JSP页面头部添加 <%@ page pageEncoding="UTF-8"%> // 数据库连接字符串配置 jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8

典型问题2:静态资源被拦截在web.xml中配置放行:

<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping>

性能优化建议:

  1. 使用连接池(如HikariCP)替代直连数据库
  2. 对频繁访问的数据添加缓存(如Redis)
  3. 静态资源启用浏览器缓存
<filter> <filter-name>expiresFilter</filter-name> <filter-class>org.apache.catalina.filters.ExpiresFilter</filter-class> <init-param> <param-name>ExpiresByType image</param-name> <param-value>access plus 1 month</param-value> </init-param> </filter>

开发效率提升:

  1. 使用JSTL标签库替代Scriptlet
<%-- 旧方式 --%> <% for(Student s : studentList) { out.print("<li>"+s.getName()+"</li>"); } %> <%-- 新方式 --%> <c:forEach items="${studentList}" var="s"> <li>${s.name}</li> </c:forEach>
  1. 采用三层架构明确职责边界:
  • Controller只负责参数接收和结果转发
  • Service处理业务逻辑和事务管理
  • DAO专注数据库操作

在团队协作中,这种分层可以让前端开发人员专注JSP/HTML开发,后端开发人员处理业务逻辑,数据库专家优化SQL语句,各司其职提升整体效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 9:38:52

teamai-cli:用命令行构建团队级AI协作与代码审查能力

很多团队在 AI 工具上投入不少&#xff0c;但真正落到日常研发流程里总觉得差点意思——每个人各聊各的&#xff0c;提示词散落在聊天记录里&#xff0c;上下文换台机器就丢了&#xff0c;代码审查也还是纯靠人肉。这个teamai-cli项目就是冲着这些痛点去的&#xff0c;把 AI 能…

作者头像 李华
网站建设 2026/9/13 9:37:47

关节力矩控制:从原理到工程落地的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 9:33:23

HDMI切换器选购避坑指南:从参数陷阱到品牌梯队一次说清

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华