news 2026/10/21 2:52:16

Java Web 入门:手写 JSP+Servlet 学生管理系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web 入门:手写 JSP+Servlet 学生管理系统

简介:这是一份面向Java Web初学者与高校课程实践者的完整学生信息管理系统源码包,基于JSP+Servlet技术栈实现MVC分层架构,覆盖Web开发核心流程与数据库交互实战。资源共576个文件,包含156个HTML页面、132个CSS样式表、79个JavaScript脚本用于前端交互与表单验证,20个Java源文件与20个编译后Class文件构成后端Servlet逻辑(如StudentListServlet、LoginServlet、GradeSaveServlet等),4个JSP页面承担视图渲染,另含1个SQL建库脚本(db_studentinfo.sql)及配套Jar依赖,整体压缩包仅2.75MB,轻量易部署。已有997人学习下载,适合在Tomcat容器中快速运行调试,助读者深入理解HTTP请求处理、JDBC数据库操作、会话管理、前后端协同及常见异常处理机制,是掌握Java Web基础开发能力的典型教学案例。

1. 一个跑在 Tomcat 里的“老派但能打”的学生信息管理系统:不靠 Spring Boot,纯 JSP + Servlet 手写 CRUD,适合 Java Web 入门者照着敲通、改透、再往上托举

你可能刚学完 Java 基础,正对着 MVC 概念发懵;也可能在培训班里被 Spring Boot 的自动配置绕晕,却连HttpServletRequest是怎么从浏览器飞进doPost()方法的都说不清楚。这时候,一份不加任何框架糖衣、全靠HttpServlet接口、request.getParameter()拿参数、response.sendRedirect()跳转、JSP 里混着<%= %>和<%-- --%>的学生信息管理系统,反而成了最硬核的“认知锚点”。它没用 MyBatis 封装 SQL,没用 Thymeleaf 渲染模板,所有增删改查逻辑都明明白白写在.java文件里,数据库连接用的是最原始的DriverManager.getConnection(),连分页都是手写LIMIT ? OFFSET ?。这不是过时,而是把 Web 开发的“毛细血管”一层层剥开给你看——HTTP 请求怎么变成 Java 对象,Java 对象怎么变成 HTML 表格,表单提交后重定向为什么比转发更安全。如果你的目标是真正理解 Java Web 底层链路,而不是只会在 IDEA 里点 Run 按钮,这份资源就是你该亲手敲一遍、打断点跟一遍、改错报错再修一遍的“血泪教材”。

2. 项目结构与技术栈:看清骨架,才能知道往哪补肉

2.1 目录结构:5 个核心文件夹撑起整个系统

项目采用标准的 Java Web 传统目录结构(非 Maven),解压后你会看到如下主干:

student-system/ ├── src/ # Java 源码根目录 │ ├── dao/ # 数据访问层:StudentDao.java(含 JDBC 连接、CRUD 方法) │ ├── entity/ # 实体类:Student.java(id, name, age, gender, classNo 等字段 + getter/setter) │ ├── servlet/ # 控制器层:AddStudentServlet.java, DeleteStudentServlet.java, │ │ # ListStudentServlet.java, UpdateStudentServlet.java, │ │ # UpdateStudentFormServlet.java(负责跳转到编辑页) │ └── util/ # 工具类:DBUtil.java(封装 getConnection()、close(),含 try-with-resources) ├── WebContent/ # Web 资源根目录(对应 WAR 包的根路径) │ ├── css/ # 样式:style.css(简单表格样式、按钮 hover 效果) │ ├── js/ # 脚本:validate.js(前端表单校验:姓名非空、年龄为数字) │ ├── WEB-INF/ # 安全目录(外部无法直接访问) │ │ ├── web.xml # 部署描述符:定义 Servlet 映射(/add → AddStudentServlet) │ │ └── lib/ # 依赖库:mysql-connector-java-5.1.47.jar(JDBC 驱动) │ ├── index.jsp # 首页:导航栏 + 学生列表入口 │ ├── list.jsp # 列表页:循环显示 Student 对象,每行带“编辑”“删除”链接 │ ├── add.jsp # 添加页:HTML 表单,method="post" action="/add" │ ├── update.jsp # 编辑页:表单预填充值,action="/update" │ └── success.jsp # 操作成功页:显示提示信息并返回列表 └── README.md # 简要说明(含数据库建表语句)

提示:WebContent是 Eclipse/MyEclipse 时代的标准 WebRoot 名称,若用 IntelliJ IDEA 导入,需在 Project Structure → Artifacts 中将WebContent设为 Web Application Resource Directory。

2.2 技术栈选型:为什么是这些“老古董”?

组件版本/实现方式为什么选它?
JavaJDK 8+兼容性好,try-with-resources简化 JDBC 资源关闭,lambda 在工具类中可选使用
Servletjavax.servlet-api3.1.0(由 Tomcat 提供)项目未打包servlet-api.jar,依赖容器提供,避免版本冲突
JSPJSP 2.3(EL 表达式支持${student.name})用 EL 替代<%= student.getName() %>,更简洁;JSTL 未引入,保持最小依赖
数据库MySQL 5.7 / 8.0DBUtil.java中 URL 为jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC,明确指定时区避免日期乱码
驱动mysql-connector-java-5.1.47.jar5.1.x 系列兼容 JDK 8,且对 MySQL 8.0 的caching_sha2_password认证兼容性较好(若报错,换 8.0.x 驱动)

注意:项目未使用连接池(如 Druid、HikariCP),每次操作新建连接。这是教学简化设计,生产环境必须替换。我在某高校实训项目中就因忽略这点,30 并发下数据库连接数瞬间打满,最后紧急替换成 HikariCP 并配了maximumPoolSize=20。

2.3 数据库设计:一张表,五个字段,足够讲清关系映射

student表结构(见README.md):

CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT CHECK (age BETWEEN 15 AND 30), gender ENUM('男', '女') DEFAULT '男', classNo VARCHAR(20) NOT NULL );
  • id为主键自增,Servlet 层无需手动赋值;
  • age加了CHECK约束,但 DAO 层仍需做 Java 层校验(防绕过前端 JS);
  • gender用ENUM而非TINYINT,JSP 中直接输出中文,避免if-else转换;
  • classNo为字符串,适配“计算机2021级1班”等真实场景,而非单纯数字。

3. 核心模块拆解:从ListStudentServlet到list.jsp,一个请求的完整生命线

3.1 控制器层:ListStudentServlet.java—— 如何把数据库数据塞进 request 域

// src/servlet/ListStudentServlet.java public class ListStudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 调用 DAO 获取所有学生列表 List<Student> students = new StudentDao().findAll(); // 2. 将数据存入 request 域(仅本次请求有效) request.setAttribute("students", students); // 3. 转发到 list.jsp(URL 不变,request 域数据可传递) request.getRequestDispatcher("/list.jsp").forward(request, response); } }
  • 关键点 1:forward()vssendRedirect()
    此处必须用forward(),因为request.setAttribute()只在当前请求生命周期内有效。若用response.sendRedirect("/list.jsp"),会发起新请求,原request域数据丢失,list.jsp中${students}将为 null。
  • 关键点 2:DAO 返回List<Student>
    StudentDao.findAll()内部执行SELECT * FROM student,用ResultSet逐行new Student()并set字段。注意rs.getInt("id")与rs.getString("name")的类型匹配,MySQLENUM字段用getString()直接读取中文。

3.2 视图层:list.jsp—— 如何用 JSTL-like 方式遍历集合(不用 JSTL)

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- 注意:此项目实际未引入 JSTL,所以用原始 scriptlet --> <html> <head><title>学生列表</title></head> <body> <h2>学生信息列表</h2> <a href="add.jsp">添加学生</a> <table border="1"> <tr> <th>ID</th><th>姓名</th><th>年龄</th><th>性别</th><th>班级</th><th>操作</th> </tr> <!-- 3.1 中 request.setAttribute("students", students) 后,此处可直接获取 --> <% List<Student> students = (List<Student>) request.getAttribute("students"); if (students != null && !students.isEmpty()) { for (Student s : students) { %> <tr> <td><%= s.getId() %></td> <td><%= s.getName() %></td> <td><%= s.getAge() %></td> <td><%= s.getGender() %></td> <td><%= s.getClassNo() %></td> <td> <a href="updateForm?id=<%= s.getId() %>">编辑</a> | <a href="delete?id=<%= s.getId() %>" onclick="return confirm('确定删除?')">删除</a> </td> </tr> <% } } else { %> <tr><td colspan="6">暂无学生信息</td></tr> <% } %> </table> </body> </html>
  • 关键点:onclick="return confirm(...)"
    删除前弹窗确认,防止误点。return false会阻止<a>的默认跳转,return true才继续执行href。这是前端最朴素的防御,比后端拦截更及时。
  • 为什么不用 JSTL?
    教学项目刻意回避标签库,强制开发者直面for循环和if判断的原始写法,理解request.getAttribute()的本质。等你手写十遍for (Student s : students),再学 JSTL 的<c:forEach>才知其价值。

3.3 数据访问层:StudentDao.java—— JDBC 模板代码的“最小可行版”

// src/dao/StudentDao.java public class StudentDao { public List<Student> findAll() { List<Student> list = new ArrayList<>(); String sql = "SELECT id, name, age, gender, classNo FROM student ORDER BY id"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Student s = new Student(); s.setId(rs.getInt("id")); s.setName(rs.getString("name")); s.setAge(rs.getInt("age")); s.setGender(rs.getString("gender")); // ENUM 字段直接读 String s.setClassNo(rs.getString("classNo")); list.add(s); } } catch (SQLException e) { e.printStackTrace(); // 教学项目打印堆栈,生产环境应记录日志并抛自定义异常 } return list; } public boolean add(Student student) { String sql = "INSERT INTO student(name, age, gender, classNo) VALUES(?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, student.getName()); ps.setInt(2, student.getAge()); ps.setString(3, student.getGender()); ps.setString(4, student.getClassNo()); int rows = ps.executeUpdate(); if (rows > 0) { // 获取自增主键(可选,用于后续关联) try (ResultSet rs = ps.getGeneratedKeys()) { if (rs.next()) { student.setId(rs.getInt(1)); } } } return rows > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } }
  • 关键点:PreparedStatement防 SQL 注入
    所有参数用?占位,ps.setString(1, ...)赋值,彻底杜绝' OR '1'='1类攻击。这是Statement和PreparedStatement的生死线。
  • 关键点:try-with-resources自动关闭
    Connection,PreparedStatement,ResultSet均实现AutoCloseable,括号内声明即自动调用close(),无需finally块。这是 JDK 7+ 的重大改进,新手常忘写close()导致连接泄漏。

4. 运行与部署:从零开始,在本地 Tomcat 上跑起来

4.1 环境准备:三步到位,拒绝玄学错误

  1. 安装 JDK 8+ 并配置JAVA_HOME
    验证:终端输入java -version,输出java version "1.8.0_301"或更高。若报command not found,检查PATH是否包含%JAVA_HOME%\bin(Windows)或$JAVA_HOME/bin(macOS/Linux)。

  2. 安装 Tomcat 9.0.x(推荐 9.0.83)

    • 下载地址:https://tomcat.apache.org/download.cgi(选zip或tar.gz)
    • 解压后,进入bin/目录:
      • Windows:双击startup.bat
      • macOS/Linux:chmod +x startup.sh && ./startup.sh
    • 浏览器访问http://localhost:8080,看到 Apache Tomcat 欢迎页即成功。
  3. 创建 MySQL 数据库并导入表

    CREATE DATABASE studentdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE studentdb; -- 粘贴 README.md 中的 CREATE TABLE 语句

提示:若 MySQL 8.0 报Public Key Retrieval is not allowed,在DBUtil.java的 JDBC URL 末尾追加&allowPublicKeyRetrieval=true。

4.2 项目导入与部署:Eclipse/IDEA 两套方案

方案一:Eclipse(最顺滑,原生支持 Dynamic Web Project)
  1. File → New → Dynamic Web Project
    • Project name:student-system
    • Target runtime: 选择已配置的 Tomcat 9
    • Dynamic web module version:3.1(Servlet 3.1)
  2. 将下载包中的src/内容复制到 Eclipse 项目的src/下;WebContent/内容复制到WebContent/下。
  3. 右键项目 →Properties → Deployment Assembly→ 确保src映射到/WEB-INF/classes,WebContent/WEB-INF/lib映射到/WEB-INF/lib。
  4. 右键项目 →Run As → Run on Server→ 选择 Tomcat → Finish。
  5. 浏览器打开http://localhost:8080/student-system/,首页出现即成功。
方案二:IntelliJ IDEA(需手动配置 Artifact)
  1. File → New → Project from Existing Sources→ 选择解压目录。
  2. 选择Import project from external model → Eclipse(即使不是 Eclipse 项目也勾选)。
  3. File → Project Structure → Artifacts→+ → Web Application: Archive → student-system.war。
    • 在Output Layout标签页,点击+ → Add Directory Content → 选择 WebContent 目录。
    • 点击+ → Add Directory Content → 选择 src 目录,并设置Output directory为WEB-INF/classes。
    • 点击+ → Add Library → 选择 mysql-connector-java-5.1.47.jar,添加到WEB-INF/lib。
  4. Run → Edit Configurations → + → Tomcat Server → Local→Deployment → + → Artifact → student-system:war exploded。
  5. 点击绿色三角形运行,浏览器访问同上。

4.3 首次运行必查的三个日志位置

  • Tomcat 控制台日志:启动时终端输出,关注INFO: Server startup in [xxx] ms和SEVERE:错误。
  • Tomcat 日志文件:logs/catalina.out(Linux/macOS)或logs/catalina.yyyy-mm-dd.log(Windows),搜索Exception或Caused by:。
  • 浏览器开发者工具(F12)→ Console 标签页:查看 JS 报错(如validate.js语法错误)、404(资源路径错)、405(Method Not Allowed,Servlet 未实现doPost)。

注意:若页面空白但控制台无报错,大概率是web.xml中 Servlet 映射路径写错,比如<url-pattern>/add</url-pattern>对应AddStudentServlet,但add.jsp表单action="/addStudent",导致 404。

5. 避坑指南:我踩过的 5 个真实坑,帮你省下 3 小时调试时间

5.1 现象:HTTP Status 404 – Not Found,但web.xml明明写了映射

原因:web.xml中<servlet-class>写成了servlet.AddStudentServlet,但实际类在src/servlet/AddStudentServlet.java,而 Eclipse/IDEA 默认将src下的包路径视为default package(无包名),导致类加载失败。
解决:在AddStudentServlet.java第一行添加package servlet;,并确保web.xml中<servlet-class>与之完全一致。血泪经验:所有 Servlet 必须显式声明 package,否则跨 IDE 移植必翻车。

5.2 现象:中文姓名存入数据库后变成??,但数据库、表、字段均为utf8mb4

原因:JDBC URL 缺少字符集参数,MySQL 驱动默认用latin1连接。
解决:修改DBUtil.java中的 URL:

private static final String URL = "jdbc:mysql://localhost:3306/studentdb?" + "useSSL=false&serverTimezone=UTC&characterEncoding=utf8mb4&useUnicode=true";

注意:characterEncoding=utf8mb4和useUnicode=true必须同时存在,缺一不可。

5.3 现象:点击“删除”后页面跳转到http://localhost:8080/delete?id=5,报 404

原因:web.xml中只配置了ListStudentServlet和AddStudentServlet,漏配DeleteStudentServlet的<servlet>和<servlet-mapping>。
解决:在web.xml中补充:

<servlet> <servlet-name>DeleteStudentServlet</servlet-name> <servlet-class>servlet.DeleteStudentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DeleteStudentServlet</servlet-name> <url-pattern>/delete</url-pattern> </servlet-mapping>

排查技巧:在DeleteStudentServlet的doGet()开头加System.out.println("DeleteServlet invoked!");,重启 Tomcat 看控制台是否打印——不打印即未注册。

5.4 现象:add.jsp提交后,AddStudentServlet.doPost()中request.getParameter("name")返回null

原因:add.jsp表单method="post",但AddStudentServlet只重写了doGet(),未实现doPost()。
解决:在AddStudentServlet.java中添加:

@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); // 复用 doGet 逻辑,或单独写业务 }

根本原因:HTTP 方法不匹配,Servlet 容器不会调用doGet()处理 POST 请求。

5.5 现象:list.jsp中${students}输出为空,但数据库有数据

原因:ListStudentServlet用了response.sendRedirect("/list.jsp")而非request.getRequestDispatcher(...).forward(...)。
解决:将sendRedirect改为forward,并确认list.jsp路径正确(/list.jsp是相对于 WebRoot 的绝对路径)。
验证方法:在ListStudentServlet.doGet()结尾加System.out.println("Students size: " + students.size());,若控制台打印0,说明 DAO 查询失败;若打印5但页面为空,则一定是forward/redirect用错。

6. 二次开发实战:从“能跑”到“能改”,三个可立即落地的增强点

6.1 增强点一:给列表页加服务端分页(不依赖前端 JS)

当前list.jsp一次性查出所有数据,数据量大时内存爆炸。我们手写一个PageBean和分页查询。

步骤 1:新增util/PageBean.java

// src/util/PageBean.java public class PageBean { private int currentPage = 1; // 当前页码 private int pageSize = 5; // 每页条数 private int totalRecords; // 总记录数 private List<Student> data; // 当前页数据 // getter/setter 省略 public int getTotalPages() { return (int) Math.ceil((double) totalRecords / pageSize); } public boolean hasPrevious() { return currentPage > 1; } public boolean hasNext() { return currentPage < getTotalPages(); } }

步骤 2:改造StudentDao.java,新增分页方法

public PageBean findPage(int pageNum, int pageSize) { PageBean pb = new PageBean(); pb.setCurrentPage(pageNum); pb.setPageSize(pageSize); // 1. 查询总记录数 String countSql = "SELECT COUNT(*) FROM student"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(countSql); ResultSet rs = ps.executeQuery()) { if (rs.next()) { pb.setTotalRecords(rs.getInt(1)); } } catch (SQLException e) { e.printStackTrace(); } // 2. 查询当前页数据(MySQL 分页语法) String dataSql = "SELECT id, name, age, gender, classNo FROM student ORDER BY id LIMIT ? OFFSET ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(dataSql)) { ps.setInt(1, pageSize); ps.setInt(2, (pageNum - 1) * pageSize); // OFFSET 从 0 开始 try (ResultSet rs = ps.executeQuery()) { List<Student> list = new ArrayList<>(); while (rs.next()) { Student s = new Student(); s.setId(rs.getInt("id")); s.setName(rs.getString("name")); s.setAge(rs.getInt("age")); s.setGender(rs.getString("gender")); s.setClassNo(rs.getString("classNo")); list.add(s); } pb.setData(list); } } catch (SQLException e) { e.printStackTrace(); } return pb; }

步骤 3:修改ListStudentServlet.java,接收页码参数

@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取页码,默认第 1 页 String pageStr = request.getParameter("page"); int pageNum = 1; if (pageStr != null && !pageStr.trim().isEmpty()) { try { pageNum = Integer.parseInt(pageStr); } catch (NumberFormatException e) { pageNum = 1; } } PageBean pb = new StudentDao().findPage(pageNum, 5); request.setAttribute("pb", pb); request.getRequestDispatcher("/list.jsp").forward(request, response); }

步骤 4:改造list.jsp,添加分页导航

<!-- list.jsp 底部追加 --> <div style="margin-top:20px;"> <c:if test="${pb.hasPrevious()}"> <a href="list?page=${pb.currentPage - 1}">上一页</a> </c:if> <span>第 ${pb.currentPage} 页,共 ${pb.totalPages} 页</span> <c:if test="${pb.hasNext()}"> <a href="list?page=${pb.currentPage + 1}">下一页</a> </c:if> </div>

注意:此处用了 JSTL 的<c:if>,需在list.jsp顶部添加<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,并将jstl-1.2.jar放入WEB-INF/lib。这是唯一引入的标签库,代价极小,收益巨大。

6.2 增强点二:用 Filter 统一处理中文乱码(告别每个 Servlet 写request.setCharacterEncoding())

步骤 1:新建filter/CharacterEncodingFilter.java

// src/filter/CharacterEncodingFilter.java public class CharacterEncodingFilter implements Filter { private String encoding = "UTF-8"; @Override public void init(FilterConfig config) throws ServletException { String enc = config.getInitParameter("encoding"); if (enc != null) encoding = enc; } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; // 设置请求编码(POST 表单) request.setCharacterEncoding(encoding); // 设置响应编码(JSP 输出) response.setContentType("text/html;charset=" + encoding); response.setCharacterEncoding(encoding); chain.doFilter(request, response); } }

步骤 2:在web.xml中配置 Filter

<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> <!-- 拦截所有请求 --> </filter-mapping>

从此,所有 Servlet 中request.getParameter()都能正确获取中文,无需重复设置。

6.3 增强点三:增加登录验证(Session 控制,非 Cookie)

步骤 1:新建servlet/LoginServlet.java

// src/servlet/LoginServlet.java public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); // 简单校验(实际应查数据库、加盐哈希) if ("admin".equals(username) && "123456".equals(password)) { // 登录成功,存入 Session HttpSession session = request.getSession(); session.setAttribute("user", username); response.sendRedirect("list"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } }

步骤 2:新建login.jsp(含表单)

<!-- WebContent/login.jsp --> <form action="login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> ${error}

步骤 3:新建filter/LoginFilter.java,拦截需要登录的资源

// src/filter/LoginFilter.java public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; String uri = request.getRequestURI(); // 放行登录页、登录 Servlet、静态资源 if (uri.contains("/login.jsp") || uri.contains("/login") || uri.endsWith(".css") || uri.endsWith(".js")) { chain.doFilter(request, response); return; } // 检查 Session 中是否有 user HttpSession session = request.getSession(false); if (session == null || session.getAttribute("user") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(request, response); } }

步骤 4:在web.xml中配置LoginFilter,并确保顺序在CharacterEncodingFilter之后

<filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

从那以后我每次接手一个老项目,第一件事就是检查web.xml里有没有CharacterEncodingFilter和LoginFilter。没有?立刻补上,这是保障系统健壮性的两条底线。哪怕只是教学项目,也要养成这种肌肉记忆——因为线上环境不会给你“下次一定”的机会。希望帮到你。

本文还有配套的精品资源,点击获取

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

Matlab六自由度机械臂仿真:运动学、控制器与避障算法实战

如果你手里正好压着这样一个项目——一台开源结构的六自由度机械臂&#xff0c;要在Matlab里把仿真环境搭起来&#xff0c;同时搞定避障算法和控制器设计&#xff0c;那这篇文章应该能帮你省掉一大段自己摸索的时间。我搭这套环境前后花了大概三周&#xff0c;中间绕了不少弯路…

作者头像 李华
网站建设 2026/10/21 2:51:35

LLM驱动的智能风险评估Agent架构与落地实践

我参与过的金融风控项目里&#xff0c;有个让人印象特别深的场景&#xff1a;业务方拿过来一家中小企业的资料&#xff0c;要求在一小时内给出初步信用评估结论。传统做法是人工翻阅财务报表、检索公开信息、电话核实关键数据&#xff0c;一套流程下来至少半天。后来有幸在一个…

作者头像 李华
网站建设 2026/10/21 2:51:32

从零构建智能试卷解析系统:基于OpenCV与PaddleOCR的完整实践

从零构建智能试卷解析系统在教育信息化的圈子里泡久了&#xff0c;你会发现一个很有意思的现象&#xff1a;老师们手里最耗时间的活儿&#xff0c;除了备课上课&#xff0c;就是批改试卷和整理错题。尤其到了期中期末&#xff0c;一摞摞试卷堆在办公桌上&#xff0c;光是把题目…

作者头像 李华
网站建设 2026/10/21 2:51:30

离线环境大模型部署实战:内网数据分析与本地推理方案

很久以前我一直觉得&#xff0c;大模型分析数据就必须走在线API&#xff0c;直到去年接到一个真正断网的内部数据处理任务&#xff0c;这个想法被彻底改写了。那台服务器放在一个没有外网的机房里&#xff0c;管理要求数据不能出内网&#xff0c;连常规软件源都不通。要在这种环…

作者头像 李华
网站建设 2026/10/21 2:51:15

GA-LSTM超参优化实战:解决LSTM预测抖动与收敛不稳问题

简介&#xff1a;本资源是一份面向人工智能与时间序列预测方向初学者及实践者的Python代码实现&#xff0c;聚焦于遗传算法&#xff08;GA&#xff09;优化LSTM模型的核心技术方案。针对传统LSTM超参数调优困难、易陷入局部最优等问题&#xff0c;该方案通过GA全局搜索能力自动…

作者头像 李华
网站建设 2026/10/21 2:50:39

ReAct Agent原理与实战:手把手构建可行动的大模型应用

接到过不少类似的问题&#xff1a;"我直接让大模型帮我查一下今天的实时行情&#xff0c;它一本正经编了个数字给我""我让它把一份表格处理后入库&#xff0c;结果它只回了好的&#xff0c;已经处理完成&#xff0c;实际上什么都没做"。问题不在于模型的&q…

作者头像 李华