news 2026/9/18 11:21:13

JavaEE宠物领养网站实战:JSP+Servlet+MySQL全流程开发解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaEE宠物领养网站实战:JSP+Servlet+MySQL全流程开发解析

简介:基于JavaEE的宠物领养网站毕业设计论文以真实课题为主线,面向计算机相关专业学生及正在准备课程设计、毕业设计的开发者,尤其适合学习JavaEE、B/S架构和分层开发的读者。论文从宠物领养的社会需求切入,完整覆盖需求分析、系统设计、数据库设计、实现与测试环节,详细阐述JSF表现层、EJB业务逻辑层、MySQL数据存储层的技术方案,并给出数据库表结构设计、优化及安全性处理思路。资源包共1个docx文档,大小2.44MB,包含中英文摘要、目录、正文和关键词,结构完整,便于直接参考论文组织框架。目前已有165人学习该资源。读者可借此梳理宠物领养平台的功能模块划分、数据库建模和系统测试方法,也能为独立完成同类JavaEE项目提供从设计到落地的全流程参照。

1. 宠物领养网站的 JavaEE 实现:从论文到可运行代码

宠物领养网站这类项目,拆开看就是一个典型的信息发布与申请审核系统。用户注册登录、发布宠物寄养信息、申请成为领养人、发布者审核、管理员维护网站动态,这套流程用 JavaEE 里的 JSP + Servlet + MySQL 就能完整落地,不需要引入微服务或者分布式中间件。我通读了这份毕业设计论文之后发现,它的价值恰恰在于把 B/S 架构、三层分层、JDBC 操作、Session 会话管理这些 JavaWeb 的基础知识点一次性串了起来,非常适合作为课程设计或毕业设计课题。项目本身不追新,核心就是搞清楚 Servlet 怎么接收请求、JSP 怎么渲染数据、MySQL 表怎么设计才能支撑领养流程。适合正在做 JavaEE 课设、准备校招面试时补 JavaWeb 基础的同学,也适合想快速搭一个带前后台权限的完整 Web 应用的一线开发。

2. 技术选型与数据库设计:JSP + Servlet + MySQL 组合怎么立住

2.1 B/S 架构在宠物领养场景下的优势

论文里对 C/S 和 B/S 做了比较,这个比较在选型时不是走形式,而是直接决定开发成本。C/S 模式需要为每个用户安装客户端程序,升级时要逐个更新,对于宠物领养这种面向社会公众、用户群体分散的场景完全不现实。B/S 模式把客户端收敛为浏览器,所有业务逻辑都部署在 Web 服务器上,用户在地址栏输入网址就能访问,管理员也通过同一个 Web 应用的后台入口做维护。

具体到宠物之家这个项目,B/S 选型的直接收益有三个:

  1. 用户零安装,注册账号后即可浏览宠物信息、提交领养申请,降低了参与门槛。
  2. 系统升级只改服务器端,发布新的 WAR 包到 Tomcat 就完成更新,不用推送客户端版本。
  3. 数据集中存储在 MySQL 服务器上,宠物信息、走失信息、领养申请的状态变更不会出现多端数据不一致。

JavaEE 体系下的 JSP + Servlet 组合,在 B/S 架构里承担了完整的请求响应链路。浏览器发起 HTTP 请求,Tomcat 容器把请求交给对应的 Servlet 处理,Servlet 调用 DAO 层访问 MySQL,然后把业务数据放入 request 或 session 作用域,最后转发给 JSP 渲染成 HTML 返回。整个过程每一层职责都很清晰,论文里用 J2EE 三层模型来描述这个结构,实际编码时对应的就是 web 层、service/dao 层和数据库层。

2.2 功能模块划分:用户与管理员两套权限

宠物之家的功能模块拆成前台和后台两套体系。前台面向注册用户,后台面向系统管理员。角色权限不通过独立的权限框架控制,而是简单的基于 Session 中用户角色的判断,这在毕业设计场景下够用,也好表达。

前台功能模块包括:

模块功能描述数据操作
用户注册登录注册新账号、登录、退出插入 user 表、查询 user 表
宠物寄养发布宠物寄养信息、浏览列表、查看详情插入 pet 表、分页查询 pet 表
领养申请对某只宠物提交领养申请、查看审核结果插入 adopt_apply 表、更新状态
宠物走失发布走失信息、浏览走失列表插入 lost_pet 表、查询 lost_pet 表
养宠经验发布经验帖子、浏览列表插入 article 表、查询 article 表
个人中心修改个人信息与密码、管理自己发布的内容更新 user 表、按 uid 查询

后台管理员功能包括用户信息管理(查看用户列表、禁用异常账号)和网站动态发布(增删改查 news 表)。后台不单独建一套前端工程,而是复用同一个 Web 应用,通过/admin/*路径前缀配合过滤器做权限拦截。

2.3 数据库逻辑结构设计:六张核心表

数据库设计是整个项目最先落地也最容易返工的部分。论文里提到了数据库逻辑结构设计,但没有给出完整的建表脚本。按这个项目的业务流,我一般会拆出六张核心表:用户表、宠物信息表、领养申请表、走失信息表、养宠经验表、网站动态表。

用户表是权限控制的基础,role 字段区分 admin 和 user 两种角色。密码不存明文,至少用 MD5 加盐存储,论文原文里没有强调这点,但实际做项目时必须处理。

CREATE TABLE `user` ( `uid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '登录用户名', `password` varchar(64) NOT NULL COMMENT 'MD5密码', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `role` varchar(10) NOT NULL DEFAULT 'user' COMMENT '角色:admin/user', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`uid`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

密码字段长度设置 64 是因为 MD5 加密后是 32 位十六进制,如果后面改用 MD5 加盐或 SHA-256,64 位长度仍然够用。username 加唯一索引,防止重复注册。角色字段用字符串而不是 int,读代码时语义更清晰,代价是多占一点存储空间,对这个量级的网站可以忽略。

宠物信息表是核心业务表,定义了status字段表示领养状态:

CREATE TABLE `pet` ( `pid` int(11) NOT NULL AUTO_INCREMENT COMMENT '宠物ID', `title` varchar(100) NOT NULL COMMENT '信息标题', `type` varchar(20) NOT NULL COMMENT '宠物类型:猫/狗/其他', `breed` varchar(50) DEFAULT NULL COMMENT '品种', `age` varchar(20) DEFAULT NULL COMMENT '年龄描述', `gender` varchar(10) DEFAULT NULL COMMENT '性别', `vaccine` tinyint(1) DEFAULT '0' COMMENT '是否已接种疫苗', `description` text COMMENT '宠物详细描述', `photo` varchar(255) DEFAULT NULL COMMENT '照片路径', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待领养 1已领养', `user_id` int(11) NOT NULL COMMENT '发布者ID', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间', PRIMARY KEY (`pid`), KEY `idx_status` (`status`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物领养信息表';

status字段单独建索引,因为列表页最常见的查询条件是「状态为待领养的宠物」。vaccine用 tinyint(1) 表达布尔值,MySQL 里比直接用 varchar 存 yes/no 更省空间,查询也更快。age用 varchar 而不是 int,原因是幼犬、成年、老年这种描述性信息更符合领养场景的实际表达习惯。

领养申请表解决的是「一只宠物可能被多人申请」的业务问题:

CREATE TABLE `adopt_apply` ( `apply_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '申请ID', `pet_id` int(11) NOT NULL COMMENT '宠物ID', `user_id` int(11) NOT NULL COMMENT '申请人ID', `reason` text COMMENT '领养理由', `status` varchar(10) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/APPROVED/REJECTED', `apply_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '申请时间', PRIMARY KEY (`apply_id`), KEY `idx_pet_id` (`pet_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='领养申请表';

申请表状态用字符串枚举而不是数字,PENDINGAPPROVEDREJECTED三个值直接在代码里写死。这个设计在业务状态少且稳定的时候比数字更可读,但要注意代码里不能出现拼写不一致,建议定义常量类统一管理。走失信息表、养宠经验表、网站动态表和宠物表结构类似,核心字段都是标题、内容、发布人、时间四项,这里不再重复列 DDL。

3. 核心功能实现:注册登录、宠物发布与领养申请

3.1 用户注册与登录:Session 会话管理

登录逻辑是第一个要写好的模块,因为它牵涉到 Session 管理、请求转发、重定向和密码校验四个基础知识点。用户提交表单后,Servlet 从请求参数中取出用户名和密码,调用 DAO 查询数据库校验,通过后把 User 对象放入 Session,失败则返回登录页并携带错误提示。

@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserDao userDao = new UserDao(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null || username.trim().isEmpty() || password == null || password.isEmpty()) { request.setAttribute("msg", "用户名和密码不能为空"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } User user = userDao.findByUsernameAndPassword(username, MD5Util.md5(password)); if (user == null) { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } HttpSession session = request.getSession(); session.setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/index.jsp"); } }

代码说明:setCharacterEncoding("UTF-8")必须放在读取第一个参数之前,否则中文用户名的乱码问题会让你排查半天。登录失败用forward而不是sendRedirect,这样request.setAttribute里的错误消息才能在login.jsp上通过${msg}正常显示。登录成功用重定向,避免用户刷新页面时重复提交表单。request.getContextPath()取到的是部署路径,比如/pet_house,拼上它才不会在换部署名后出现 404。

注册逻辑与登录类似,区别在于插入前要先查一次用户名是否已存在,捕获DuplicateKeyException也能兜底,但主动查询返回给用户的提示信息更友好。密码存储用MD5Util.md5(password),论文里没有具体实现,我一般用MessageDigest封装一个工具类,加盐时再拼一个固定字符串。

3.2 宠物寄养信息发布与列表展示

发布宠物信息的前提是用户已登录。Servlet 里先从 Session 取出loginUser,如果取不到说明会话已过期,直接重定向到登录页。发布表单里包含标题、类型、品种、年龄、性别、疫苗状态、描述和照片字段,其中照片在毕业设计场景下通常存路径而不是二进制数据,图片文件上传到服务器的/upload目录,数据库只保存相对路径。

@WebServlet("/pet/add") public class PetAddServlet extends HttpServlet { private PetDao petDao = new PetDao(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); HttpSession session = request.getSession(); User loginUser = (User) session.getAttribute("loginUser"); if (loginUser == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } Pet pet = new Pet(); pet.setTitle(request.getParameter("title")); pet.setType(request.getParameter("type")); pet.setBreed(request.getParameter("breed")); pet.setAge(request.getParameter("age")); pet.setGender(request.getParameter("gender")); pet.setVaccine("on".equals(request.getParameter("vaccine")) ? 1 : 0); pet.setDescription(request.getParameter("description")); pet.setPhoto(request.getParameter("photoPath")); pet.setUserId(loginUser.getUid()); pet.setStatus(0); boolean result = petDao.insert(pet); if (result) { response.sendRedirect(request.getContextPath() + "/pet/list"); } else { request.setAttribute("msg", "发布失败,请稍后重试"); request.getRequestDispatcher("/pet/add.jsp").forward(request, response); } } }

这里有个容易踩的细节:单选框如果没选中,request.getParameter("vaccine")返回 null,用"on".equals(...)这种写法可以避免空指针。setStatus(0)表示新发布的宠物默认进入待领养列表,管理员和发布者都可以在后续把状态改为 1,即已领养。

列表页用 JSP + JSTL 渲染。分页查询是列表页的基本功,每页 10 条数据,首页传入pageNum参数:

<c:forEach items="${petList}" var="pet"> <div class="pet-card"> <h3><a href="${pageContext.request.contextPath}/pet/detail?pid=${pet.pid}">${pet.title}</a></h3> <p>类型:${pet.type} 品种:${pet.breed}</p> <p>年龄:${pet.age} 性别:${pet.gender}</p> <c:if test="${pet.status == 0}"> <a href="${pageContext.request.contextPath}/adopt/apply.jsp?pid=${pet.pid}">申请领养</a> </c:if> <c:if test="${pet.status == 1}"> <span>已被领养</span> </c:if> </div> </c:forEach>

JSTL 的c:forEach循环渲染宠物卡片列表,c:if根据status字段控制按钮显示。注意链接中的pid参数需要在前端做一次数字校验,否则恶意用户传一个非数字值时,DAO 层的Integer.parseInt会直接抛异常。

3.3 领养申请与审核流程

领养申请是整个系统业务闭环的核心。申请人在宠物详情页点击「申请领养」,填入理由后提交。后端要做三重校验:是否登录、宠物是否存在、当前用户是否已经申请过这只宠物。校验通过后写入一条状态为PENDING的申请记录,宠物发布者在个人中心的「我收到的申请」里看到列表,选择通过或拒绝。

@WebServlet("/adopt/apply") public class AdoptApplyServlet extends HttpServlet { private AdoptDao adoptDao = new AdoptDao(); private PetDao petDao = new PetDao(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); User loginUser = (User) request.getSession().getAttribute("loginUser"); if (loginUser == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } int petId; try { petId = Integer.parseInt(request.getParameter("petId")); } catch (NumberFormatException e) { request.setAttribute("msg", "参数错误"); request.getRequestDispatcher("/pet/list").forward(request, response); return; } // 校验宠物是否存在 Pet pet = petDao.findById(petId); if (pet == null) { request.setAttribute("msg", "宠物信息不存在"); request.getRequestDispatcher("/pet/list").forward(request, response); return; } // 校验是否已申请过 if (adoptDao.existsApply(petId, loginUser.getUid())) { request.setAttribute("msg", "您已经申请过这只宠物了"); request.getRequestDispatcher("/pet/detail?pid=" + petId).forward(request, response); return; } AdoptApply apply = new AdoptApply(); apply.setPetId(petId); apply.setUserId(loginUser.getUid()); apply.setReason(request.getParameter("reason")); apply.setStatus("PENDING"); if (adoptDao.insert(apply)) { response.sendRedirect(request.getContextPath() + "/adopt/myApply"); } else { request.setAttribute("msg", "申请提交失败"); request.getRequestDispatcher("/adopt/apply.jsp?pid=" + petId).forward(request, response); } } }

existsApply这个校验最容易漏掉。如果不做重复性检查,用户可以反复提交申请,发布者的审核列表会被刷屏。DAO 层通过SELECT COUNT(*) FROM adopt_apply WHERE pet_id=? AND user_id=? AND status != 'REJECTED'来判断,注意把已拒绝的历史申请排除在外,允许用户被拒绝后再次申请。

审核接口在发布者的个人中心触发,更新adopt_apply.statusAPPROVEDREJECTED。通过时还要顺带把对应宠物表的status改为 1,这两个操作必须放在同一个事务里:

public boolean approveApply(int applyId, int petId) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); int rows = updateApplyStatus(conn, applyId, "APPROVED"); int rows2 = updatePetStatus(conn, petId, 1); conn.commit(); return rows > 0 && rows2 > 0; } catch (SQLException e) { rollback(conn); return false; } finally { DBUtil.close(conn, null, null); } }

事务处理是这个功能的隐藏考点。如果只更新申请状态而忘记同步宠物状态,就会出现「申请通过但宠物还挂在待领养列表」的数据不一致问题。

4. JDBC 连接池与 DAO 封装:别让数据库操作拖垮整个项目

4.1 为什么直接用 DriverManager 行不通

初版项目最容易出现的问题,是每个 DAO 方法里都写一遍DriverManager.getConnection(),用完再close()。这种写法在单用户测试时没感觉,一旦多人同时访问,每次请求都要经历「加载驱动 — 建立 TCP 连接 — 鉴权 — 释放」的完整过程。MySQL 的连接建立是重量级操作,高并发下连接数会快速打满,数据库直接拒绝新请求。

我一般用 C3P0 连接池解决这个问题。连接池启动时预创建一批连接放在池子里,请求来了从池中取出,用完归还而不是关闭。这份论文没有指定连接池方案,C3P0 是 JSP + Servlet 项目里的常见选择,配置简单,和 MyEclipse 集成也顺。

4.2 连接池参数配置与数据库连接工具类

C3P0 支持Java 属性文件和编码两种配置方式。属性文件适合固定配置,编码方式适合部署时临时调整。下面是配合db.properties的推荐配置:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/pet_house?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=root c3p0.initialPoolSize=5 c3p0.minPoolSize=5 c3p0.maxPoolSize=20 c3p0.maxIdleTime=1800 c3p0.checkoutTimeout=3000

参数说明:initialPoolSize是连接池启动时创建的连接数,设 5 足够;maxPoolSize是池中最大连接数,课设项目并发量不大,20 是安全值;maxIdleTime控制空闲连接多少秒后被动释放,设 1800 秒可以避免长期空闲占用数据库资源。serverTimezone=Asia/Shanghai是 MySQL 8.0 的硬性要求,不写的话驱动会用默认时区,和本地时间对不上,查出来的日期字段会有 8 小时的偏移。

public class DBUtil { private static ComboPooledDataSource dataSource; static { try { Properties props = new Properties(); InputStream in = DBUtil.class.getClassLoader() .getResourceAsStream("db.properties"); props.load(in); dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(props.getProperty("jdbc.driver")); dataSource.setJdbcUrl(props.getProperty("jdbc.url")); dataSource.setUser(props.getProperty("jdbc.username")); dataSource.setPassword(props.getProperty("jdbc.password")); dataSource.setInitialPoolSize( Integer.parseInt(props.getProperty("c3p0.initialPoolSize"))); dataSource.setMaxPoolSize( Integer.parseInt(props.getProperty("c3p0.maxPoolSize"))); dataSource.setMaxIdleTime( Integer.parseInt(props.getProperty("c3p0.maxIdleTime"))); dataSource.setCheckoutTimeout( Integer.parseInt(props.getProperty("c3p0.checkoutTimeout"))); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

静态代码块保证连接池只在类加载时初始化一次。ExceptionInInitializerError不是随便选的——如果连接池初始化失败,这里抛出的错误会在第一次触发,随后任何用到DBUtil的请求都会立刻暴露问题,方便在部署阶段就发现配置错误。

4.3 PreparedStatement 防注入与 DAO 封装

DAO 层用PreparedStatement而不是Statement拼字符串,这是底线。宠物搜索功能如果支持按关键字模糊查找,用字符串拼接很容易被构造出意外的 SQL:

public List<Pet> searchByTitle(String keyword) { String sql = "SELECT * FROM pet WHERE title LIKE ? AND status = 0"; List<Pet> list = new ArrayList<>(); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Pet pet = new Pet(); pet.setPid(rs.getInt("pid")); pet.setTitle(rs.getString("title")); pet.setType(rs.getString("type")); pet.setBreed(rs.getString("breed")); pet.setAge(rs.getString("age")); pet.setStatus(rs.getInt("status")); list.add(pet); } } } catch (SQLException e) { e.printStackTrace(); } return list; }

LIKE ?配合setString的写法,让模糊查询的关键字只作为参数值传入,不会参与 SQL 语法解析,从根上杜绝了' OR '1'='1这类注入攻击。JDBC 的 try-with-resources 写法要求ConnectionPreparedStatementResultSet都实现AutoCloseable接口,这样即使 SQL 执行抛出异常,资源也会自动关闭,不需要再写三层嵌套的 finally。

DAO 封装到这里,Servlet 层拿到的已经是一个业务对象Pet,而不是散装的字段列表。业务逻辑变更时,比如给pet表加一个「是否绝育」字段,只需要改Pet实体类、PetDao的 SQL 和 JSP 展示三处,控制层代码不用动。这个分层习惯是 JavaEE 项目的核心,也是后面学 SSM 框架时理解 Spring 管理 Bean 的前提。

5. 部署、测试与排错:Tomcat 环境下把项目跑稳

5.1 开发环境配置要点

论文里指定了 MyEclipse 10 作为开发工具。这个版本内置了 JDK 和 Tomcat 的集成配置,导入项目后需要确认三处:项目的 Java Compiler 级别和本机 JDK 版本一致,Targeted Runtimes 里勾选对应 Tomcat 版本,部署的 Context Path 和数据库连接串保持一致。如果你习惯用 VSCode 配置 JavaEE 语言环境,思路也一样,本质是解决编译 JDK、Web 容器、依赖 jar 三个路径的匹配问题,MyEclipse 胜在 JSP 调试不需要额外配置。

5.2 测试用例设计与验证结果

系统各功能模块在 Tomcat 上运行后,需要按功能点补充测试用例。下面是我在本地跑通的六组核心用例:

用例编号测试功能操作步骤预期结果
TC01用户注册表单填写用户名、密码、联系方式注册成功,写入 user 表
TC02重复注册再次提交相同用户名提示用户名已存在
TC03宠物发布登录后填写宠物信息并提交列表页出现新宠物卡片
TC04领养申请未登录用户点击申请领养跳转登录页
TC05重复申请已申请用户再次申请同一宠物提示已申请过
TC06审核通过发布者批准领养申请宠物状态变为已领养

用例 TC04 验证的是 Session 拦截是否生效,TC05 验证的是业务层的幂等校验。毕业设计答辩时,这组用例能直接回答「你怎么证明系统是可靠的」这个问题。

5.3 三个容易踩的坑

第一个坑是 MySQL 8.0 驱动与时区。驱动类名要用com.mysql.cj.jdbc.Driver,旧版的com.mysql.jdbc.Driver会报警告,连接串必须带serverTimezone=Asia/Shanghai,否则报CST相关异常。第二个坑是中文乱码。JSP 页面头部、Servlet 的setCharacterEncoding、数据库连接串、MySQL 表字符集四处要统一为 UTF-8。最隐蔽的是 MySQL 表的CHARSET建表时忘了指定,继承库默认的 latin1,页面怎么改都是问号。

处理方式是在web.xml里加全局字符编码过滤器:

<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

第三个坑是重定向路径。发布的上下文路径如果配成了/petHouse,而代码里相对路径写死了/pet_house,部署后所有sendRedirect都会 404。统一用request.getContextPath()动态拼接,不要写死上下文名。修改 Context Path 后,同时清理 Tomcat 的 work 目录缓存,否则改过的 JSP 可能不起作用。这个项目跑通之后,你再去回头理解 Spring MVC 的DispatcherServlet和 MyBatis 的连接管理,会明显感觉容易许多。

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

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

Unity AssetBundle构建慢的三大底层原因与架构级解决方案

1. 这不是“怎么加载资源”的问题&#xff0c;而是“为什么每次改个贴图就打包两小时”的真相Unity项目做到中后期&#xff0c;美术扔来一张新纹理&#xff0c;你点下Build AssetBundle&#xff0c;看着进度条卡在“Writing asset bundle…”不动&#xff0c;咖啡凉了三杯&…

作者头像 李华
网站建设 2026/9/18 11:19:11

Ubuntu安装Docker与Docker Compose全攻略:从原理到实战避坑

最近这阵子好几个朋友都在折腾Ubuntu&#xff0c;要么是给老笔记本装了双系统&#xff0c;要么是在虚拟机上开了一台纯净的服务器版做实验。结果装完系统后第一个动作几乎都一样&#xff1a;跑来问我Docker怎么装。确实&#xff0c;现在很多服务的标准交付方式就是容器&#xf…

作者头像 李华
网站建设 2026/9/18 11:18:16

脑数据资产化:技术实现与隐私保护方案

1. 脑数据资产化的技术实现路径作为一名在数据安全领域工作多年的工程师&#xff0c;我最近深入研究了一个极具争议性的话题——如何将人类脑数据转化为可交易的数字资产。这个看似科幻的概念&#xff0c;实际上已经具备了初步的技术可行性。让我们从技术栈的构建开始&#xff…

作者头像 李华
网站建设 2026/9/18 11:16:51

【ComfyUI】SD3.5 + ControlNet 模糊引导图生图

今天展示的案例是一个基于 Stable Diffusion 3.5 与 ControlNet 的 ComfyUI 工作流,整个流程通过加载预训练模型、结合文本提示、图像输入以及控制网络,实现了对复杂画面的精准生成与细节控制。 该工作流特别适合需要在保持画面结构稳定的前提下,对生成效果进行多维度约束和…

作者头像 李华
网站建设 2026/9/18 11:16:23

Unity发热优化实战:GC、Draw Call与Canvas重建的CPU陷阱

手机厂家这些年把散热堆得再猛&#xff0c;也架不住游戏内几个毫秒的CPU峰值负载把机身烤成暖手宝。很多团队查发烫问题时习惯性把矛头指向GPU&#xff0c;但拿Unity出的包在真机上跑一遍Profile&#xff0c;你会发现CPU经常比GPU更忙。尤其是GC、Draw Call和Canvas重建这三个隐…

作者头像 李华
网站建设 2026/9/18 11:16:19

Unity Shader实现UI流光效果:原理、参数调优与移动端性能优化

你可能在游戏里见过这种效果&#xff1a;一张卡牌上有一道亮光从左下扫到右上&#xff0c;然后过几秒又来一次&#xff1b;或者一个武器图标上&#xff0c;高光条不停从表面滑过。这个就是典型的“流光效果”&#xff0c;在UI界面、装备图标、技能按钮上非常常见。很多人第一反…

作者头像 李华