Servlet+JSP+MySQL打造艺术品展览管理系统:从建表到部署全流程
发布时间:2026/10/1 10:39:19来源:尧图网络
简介一套基于ServletJspMySQL的艺术作品展览管理系统源代码及配套数据库面向Java Web初学者和需要搭建线上展览平台的管理者解决艺术作品的数字化展示、展览信息维护与用户互动管理等问题。压缩包共包含50个文件以18个Java源码、15个JSP页面、12个XML配置文件为主体另有1个SQL数据库脚本整体仅52KB结构紧凑便于快速部署与二次开发。系统内置艺术作品表、展览信息表、用户信息表三大核心数据表覆盖作品详情、展览日程与用户登录记录等关键模块代码注释详尽可直观学习Servlet请求处理、JSP页面渲染以及通过JDBC操作MySQL的完整流程。目前已有52人浏览学习适合作为课程设计、毕业设计或入门实战的参考案例在此基础上可方便地扩展在线评论、预约看展等个性化功能。1. 一套能跑通全流程的艺术品展览管理系统ServletJSPMySQL怎么分工搞过JavaWeb课设或毕设的人对基于ServletJspMySQL的艺术作品展览管理系统这个组合一定不陌生一套标准的传统JavaWeb单体应用用Servlet接请求、用JSP渲染页面、用MySQL存业务数据。它的价值在于足够简单、足够透明每一个请求从浏览器到数据库再回到浏览器的路径都能被完整看到特别适合用来理解JavaWeb的底层运行机制也适合作为毕业设计、课程实训和入门项目的底座。这套系统的业务范围一般包括展览信息发布、作品展示、用户注册登录、展览预约/购票、评论留言和后台管理。对新手来说它比纯图书管理多了“展览—展品—艺术家—订单”这种多表关联的业务模型比电商系统又少了复杂的支付和库存复杂度刚好卡在能独立思考、又能在一两个星期内做出来的位置。本文按我做这类项目时的真实顺序来拆先讲选型理由和工程骨架再给数据库表结构和建表SQL接着落Servlet和JSP的代码最后把乱码、SSL连接、war打包这类高频坑一次说清。2. 为什么这套三件套还值得做运行结构、工程骨架与适用边界2.1 技术选型不是怀旧ServletJSPMySQL的适用场景很多人在选题时会犹豫现在都Spring Boot了为什么还要写Servlet和JSP我一般会反问一句你是想学框架还是想学Web运行原理ServletJSP这套东西本质上就是Java Web最原始的运行模型——Servlet处理请求逻辑JSP负责展示JDBC连数据库。Spring MVC再高级底层也是从一个DispatcherServlet开始的MyBatis再方便底层还是JDBC那一套Connection、Statement、ResultSet。把这个三件套做明白后面看任何框架的源码和报错堆栈都能快很多。适用人群上这套方案是JavaWeb方向课设、毕设、实训项目的常客这在很多实训平台和论坛的选题热度里可以看得很清楚。适用请求量上单机部署、日均几百到几千次访问的展览信息站完全够用。它不适合高并发互联网产品但作为教学项目、内部管理系统和个人作品集它是成本和可解释性最平衡的方案。2.2 标准的工程骨架包结构怎么分才能好答辩不用Maven直接用IDEA或Eclipse建一个Dynamic Web Project包结构按“控制层—业务层—数据访问层—工具类”划分即可。我一般会建这样一个目录src/ ├── com.artexpo.servlet # 控制层登录、注册、展览、展品、订单 ├── com.artexpo.service # 业务层参数校验、业务规则 ├── com.artexpo.dao # 数据访问层JDBC操作 ├── com.artexpo.entity # 实体类User、Exhibition、Artwork、Order ├── com.artexpo.util # 工具类DBUtil、PageBean、StringUtil └── com.artexpo.filter # 过滤器编码、登录验证 WebContent/ ├── index.jsp # 入口首页 ├── login.jsp / register.jsp ├── exhibition_list.jsp ├── exhibition_detail.jsp ├── artwork_detail.jsp ├── admin/ # 后台管理页面 └── WEB-INF/ └── web.xml分层时最需要注意的一点是Servlet里不要直接拼SQL最少要经过一层Service。我见过很多同学把JDBC代码直接写在Servlet里跑通没问题但写第二个功能时就开始复制粘贴改一个字段要改三处。分层不是为了装模作样是为了让“展览列表”和“展览详情”这种相近功能复用同一套数据访问代码。2.3 两种常见开发环境IDEA与VSCode的配置差异课堂演示大多用IDEA新建JSP项目勾选Web Application模板后IDEA会自动生成web.xml和WebContent结构配好Tomcat后直接运行。用VSCode写Servlet则是另一种常见形态需要手动安装Java Extension Pack、Tomcat for Java插件还要在项目里加入servlet-api.jar作为编译依赖。VSCode轻量适合写单文件调试IDEA集成度高适合完整跑项目。新人更推荐IDEA省去不少环境折腾的时间。3. 数据库设计先行展览、展品、用户与订单的建表SQL和必调参数3.1 需求到表结构一个展览管理系统需要几张表设计数据库时我习惯先列出业务句子再从中抽实体。这个系统里的核心业务句是管理员发布展览参展方提交作品参加某场展览普通用户注册登录后查看展览和作品、预约观展、发表评论。抽出来就是六个实体管理员、普通用户、参展方、展览、作品、预约/订单再加上评论。展览和作品是一对多作品和参展方是多对一用户和订单是一对多订单和展览是多对一。设计表时把外键关系建清楚后面做列表联查就不用在Java代码里写拼接逻辑。下面是这个系统最常用的四张核心表我用MySQL 8.0来建-- 用户表同时服务管理员与普通用户用role字段区分 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT DEFAULT 1 COMMENT 1-普通用户2-管理员3-参展方, real_name VARCHAR(50), phone VARCHAR(20), email VARCHAR(100), status TINYINT DEFAULT 0 COMMENT 0-正常1-禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 展览表 CREATE TABLE exhibition ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, theme VARCHAR(200), cover_img VARCHAR(500), intro TEXT, start_date DATE, end_date DATE, location VARCHAR(200), status TINYINT DEFAULT 0 COMMENT 0-未开始1-进行中2-已结束, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 作品表 CREATE TABLE artwork ( id INT PRIMARY KEY AUTO_INCREMENT, exhibition_id INT NOT NULL, artist_id INT, title VARCHAR(200) NOT NULL, category VARCHAR(50), image_url VARCHAR(500), description TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_artwork_exhibition FOREIGN KEY (exhibition_id) REFERENCES exhibition(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 预约/订单表 CREATE TABLE visit_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id INT NOT NULL, exhibition_id INT NOT NULL, visit_date DATE, ticket_count INT DEFAULT 1, status TINYINT DEFAULT 0 COMMENT 0-已预约1-已取消2-已完成, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES sys_user(id), CONSTRAINT fk_order_exhibition FOREIGN KEY (exhibition_id) REFERENCES exhibition(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意这里有两个高频检索词直接对应的设置字符集用utf8mb4而不是utf8因为展品名称里可能包含生僻字、特殊符号甚至emojistatus字段设置默认值为0避免插入数据时因漏填状态列而报错或写入空值。这也是很多真实项目里出现“状态字段为空导致列表判断失效”的根源之一。3.2 JDBC连接参数useSSL和characterEncoding两个必调项MySQL 8.0的默认认证插件是caching_sha2_password驱动要换成com.mysql.cj.jdbc.Driver连接串里建议显式关闭SSL并指定字符集。很多新手直接连的时候报错信息会指向SSL或时区问题实际上排查一下连接串多半就能发现原因。这两个参数在JDBC连接串里必须写清楚private static final String URL jdbc:mysql://localhost:3306/art_expo?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8allowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 你的密码;allowPublicKeyRetrievaltrue这个参数容易被忽略。MySQL 8.0用caching_sha2_password认证时客户端第一次连接需要从服务端获取公钥如果JDBC驱动版本较老或参数没放开会直接抛出Public Key Retrieval is not allowed的异常。这三个参数加在一起能解决90%的MySQL 8.0连接问题。3.3 连接池选型C3P0还是Druid课设和毕设里最常见的连接池是C3P0和Druid。C3P0胜在配置简单单独一个c3p0-config.xml文件就行和Spring、Hibernate的整合资料很多Druid胜在自带监控页面和SQL防火墙适合作为项目亮点写进答辩。如果只是把系统跑通C3P0够用如果想让老师看到“数据库连接池监控”这个点Druid的StatViewServlet能直接展示SQL执行次数和慢查询加分效果明显。下面是我的C3P0配置写法直接放在src根目录的c3p0-config.xml里c3p0-config named-config nameartexpo property namejdbcUrljdbc:mysql://localhost:3306/art_expo?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8/property property nameuserroot/property property namepassword123456/property property namedriverClasscom.mysql.cj.jdbc.Driver/property property nameinitialPoolSize5/property property nameminPoolSize5/property property namemaxPoolSize20/property property namemaxIdleTime60/property property nameacquireIncrement2/property property namecheckoutTimeout5000/property /named-config /c3p0-config参数含义和调整逻辑initialPoolSize和minPoolSize保持5就行系统启动时会预建5个连接maxPoolSize设20如果并发预约的峰值较高可以调到50但注意MySQL默认max_connections是151连接池上限不能超过数据库上限checkoutTimeout是获取连接的超时时间设5000毫秒超过就报错而不是无限等待maxIdleTime设置连接空闲回收时间防止数据库wait_timeout把空闲连接切断后连接池还继续往出拿。数据库的wait_timeout默认28800秒也就是8小时如果连接池空闲超过这个时间连接池里的连接会被MySQL服务端断开这时第一次请求会报连接失效。maxIdleTime低于wait_timeout即可60秒是个稳妥值。3.4 初始化数据一个可直接执行的SQL脚本建完表后要插入一笔管理员账号和一场测试展览不然项目跑起来登录入口都没有。管理员密码我建议先用明文跑通登录后再改成SHA-256加盐。下面这段初始化脚本可以直接执行INSERT INTO sys_user (username, password, role, real_name) VALUES (admin, 123456, 2, 系统管理员); INSERT INTO exam (title, theme, start_date, end_date, location) VALUES (2024当代青年艺术邀请展, 青年·城市·未来, 2024-06-01, 2024-08-31, 市美术馆3号展厅);如果用的是MySQL 5.7以前的版本utf8mb4在个别字符集配置上会有限制建议优先选择MySQL 8.0并按3.1节的表结构来建避免后续排序规则不一致导致的联查报错。4. 代码怎么落地连接工具、访问控制和JSP页面的分工实现4.1 DBUtil连接工具类把获取连接和关闭连接收口数据库操作的第一步是一个统一的DBUtil。它的职责只有两个从C3P0连接池拿连接、统一释放资源。这样每个DAO方法里不需要重复写Class.forName、getConnection和try-catch-finally代码量能减少三分之一也避免忘记关闭连接导致的连接池耗尽。package com.artexpo.util; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.mchange.v2.c3p0.ComboPooledDataSource; public class DBUtil { private static ComboPooledDataSource cpds new ComboPooledDataSource(artexpo); public static Connection getConnection() throws SQLException { return cpds.getConnection(); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void close(Connection conn, Statement stmt) { close(conn, stmt, null); } }注意这里用的是ComboPooledDataSource的无参构造加new ComboPooledDataSource(artexpo)这个字符串参数必须与c3p0-config.xml里named-config的name属性一致否则会走默认配置而不是我们自定义的池参数。关闭连接时从连接池拿出的Connection调用close()并不是真正断开数据库连接而是把连接归还给池子所以即使频繁开关连接也不用担心性能。4.2 登录与会话管理Servlet接管请求Session记录登录态登录功能是整个系统安全性的第一道门。常见做法是LoginServlet在doPost里接收username和password调用UserService的login方法查询数据库验证成功后把用户信息放进session然后重定向到首页验证失败则请求转发回login.jsp并携带错误提示。下面是一个可参考的实现package com.artexpo.servlet; import com.artexpo.dao.UserDao; import com.artexpo.entity.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; WebServlet(/login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); if (username null || password null || username.trim().isEmpty() || password.trim().isEmpty()) { req.setAttribute(error, 用户名和密码不能为空); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, user); session.setMaxInactiveInterval(30 * 60); resp.sendRedirect(req.getContextPath() /index.jsp); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }几个参数说明req.setCharacterEncoding(UTF-8)必须在读取任何参数之前调用放在最前面session.setMaxInactiveInterval(30 * 60)设置闲置超时30分钟单位是秒登录失败用forward转发而不是sendRedirect因为forward能携带request域里的error属性而redirect会丢失登录成功用sendRedirect是为了避免刷新页面时表单重复提交。这是一个非常典型的Post/Redirect/Get模式答辩时可以作为“如何避免表单重复提交”的答案。4.3 展览列表与模糊查询传参、联查和页面渲染展览列表页是首页的核心呈现。通过在ExhibitionDao里写一个支持多条件查询的方法可以将标题关键词、展览状态作为可选参数传入。JDBC里拼接动态SQL时用StringBuilder逐个追加条件并绑定参数能防止字段名被注入。public ListExhibition searchExhibitions(String keyword, String status) { ListExhibition list new ArrayList(); StringBuilder sql new StringBuilder( SELECT e.*, COUNT(a.id) AS artwork_count FROM exhibition e LEFT JOIN artwork a ON e.id a.exhibition_id ); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append(WHERE e.title LIKE ? ); params.add(% keyword.trim() %); } if (status ! null !status.trim().isEmpty()) { sql.append(keyword null || keyword.trim().isEmpty() ? WHERE : AND ); sql.append(e.status ? ); params.add(Integer.parseInt(status)); } sql.append(GROUP BY e.id ORDER BY e.create_time DESC); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { for (int i 0; i params.size(); i) { ps.setObject(i 1, params.get(i)); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Exhibition e new Exhibition(); e.setId(rs.getInt(id)); e.setTitle(rs.getString(title)); e.setStartDate(rs.getDate(start_date)); e.setEndDate(rs.getDate(end_date)); e.setLocation(rs.getString(location)); e.setArtworkCount(rs.getInt(artwork_count)); list.add(e); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码里用到了LEFT JOIN和GROUP BY画出作品的统计数量。要注意使用LEFT JOIN时即使某场展览还没有作品artwork_count也会返回0而不会丢行。LIKE模糊查询在关键词两侧拼接%号匹配任意位置的字符这种写法无法利用普通索引但对于课设级数据量完全够用。用PreparedStatement的setObject逐个绑定参数尽量不拼接字符串这样能避免SQL注入。4.4 JSP列表页JSTL遍历比脚本片段更规范JSP页面里直接写% for(...) { %不是不行但是页面会很乱。建议引入JSTL的c:forEach遍历集合页面结构与HTML风格更统一。先在WebContent/WEB-INF/lib下引入jstl-1.2.jar然后在页面顶部写taglib指令。% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % table classtable thead trth展览名称/thth展期/thth地点/thth作品数/thth操作/th/tr /thead tbody c:forEach items${exhibitionList} varitem tr td${item.title}/td td fmt:formatDate value${item.startDate} patternyyyy-MM-dd / 至 fmt:formatDate value${item.endDate} patternyyyy-MM-dd / /td td${item.location}/td td${item.artworkCount}/td td a href${pageContext.request.contextPath}/exhibition/detail?id${item.id}查看详情/a a href${pageContext.request.contextPath}/order/create?exhibitionId${item.id}预约观展/a /td /tr /c:forEach /tbody /table页面里所有的动态链接都加了${pageContext.request.contextPath}也就是部署后的应用上下文路径。不加这个前缀在IDEA内置Tomcat里跑是正常的一旦把war包丢到Tomcat的webapps下路径就变成/art_expo开头原来的链接全部404。这是一条值得记住的细节。fmt:formatDate用来格式化日期避免页面直接显示数据库里的yyyy-MM-dd HH:mm:ss原始值。GET请求访问详情页时记得在Servlet中设置响应编码为UTF-8防止中文标题出现乱码。4.5 对比一下直接JDBC与JPA方案的取舍这套系统还可以用MyBatis或Spring Data JPA来做数据访问代码量会少一些但调试时会发现报错堆栈直接指向框架内部。直接用JDBC的最大好处是每一个SQL都看得见摸得着出了错定位到具体哪一行几乎一目了然这对第一次接触JavaWeb的人来说是非常必要的学习过程。做工程化应用再上MyBatis做这套系统时先用基本功把它写透是更合理的路径。5. 传统JSP项目避坑从乱码到SSL连接再到war打包的5个现场记录5.1 MySQL SSL连接错误c3p0启动时明明配置没问题运行却报SSL异常现象Tomcat启动不报错第一次访问登录接口时控制台抛出Communications link failure或SSLHandshakeException提示信息长到看不清具体原因。原因MySQL 8.0服务端默认开启SSL客户端驱动默认也会尝试协商加密连接。如果服务端的SSL证书不受信任握手阶段就会失败。c3p0-config.xml里jdbcUrl如果没写useSSLfalse驱动会尝试开启SSL。解决在c3p0-config.xml的jdbcUrl中写入useSSLfalseallowPublicKeyRetrievaltrue同时确认serverTimezone与本地时区一致比如serverTimezoneAsia/Shanghai。这是该报错最常见的两种结果。修改后重启Tomcat即可。这个坑在MySQL 8.0出现后非常典型很多人以为是驱动问题实际上驱动没做错什么只是没有显式告诉它“这里不需要SSL”。5.2 页面中文全是问号JSP、Servlet、数据库三层各有一套编码现象浏览器里展示的展览名称显示为?????但数据库中实际存的是正确中文。原因编码不统一。JSP页面pageEncoding和contentType不一致Servlet里没设置req.setCharacterEncoding(UTF-8)数据库连接串里没带characterEncodingutf8三层各用各的编码中文就会在某个环节变成问号。解决三层统一成UTF-8。JSP页面用% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %Servlet的doPost开头写req.setCharacterEncoding(UTF-8)数据库连接串加characterEncodingutf8MySQL表的CHARACTER SET在3.1节已经设置成utf8mb4。四件事做完乱码问题基本绝迹。也可以用Filter统一处理编码写一个EncodingFilter类在web.xml里配置/*路径比在每个Servlet里手动设置更省事。5.3 web.xml版本与Servlet注解冲突访问Servlet报404或500现象项目跑起来后首页能打开但所有Servlet请求都返回404或控制台提示ClassNotFoundException。原因IDEA新建Dynamic Web Project时会生成一个web.xml不同版本对应的Servlet规范不一样。Servlet 3.0以上支持WebServlet注解如果web.xml版本是2.5注解可能不生效反之如果既用了注解又在web.xml里重复配置了同一个URL映射容器会报映射冲突部署失败。解决新建项目时选择Servlet 4.0或3.1版本使用注解方式定义Servlet时不要同时在web.xml里配置同一个servlet-mapping。如果是已有项目统一点要么全部用注解要么全部用web.xml配置。还有一种情况是web.xml文件名或位置不对必须放在WebContent/WEB-INF/web.xmlIDEA老版本偶尔创建项目失败右键项目添加Web Facets后重新生成即可。5.4 JSP页面过大导致编译失败too large的报错现象后台管理页面内容很多启动不报错首次访问该页面时Tomcat报org.apache.jasper.JasperException: JSP file too large或者直接抛APPLICATION_ALLOCATED_PAGE_TOO_LARGE。原因JSP翻译成Java文件时Tomcat默认对单个JSP文件大小有限制。早期Tomcat版本中单个JSP超过64KB就会出问题。解决把页面拆分成多个片段。常见做法是把公共的页头、页脚做成include片段用% include filecommon/header.jsp %静态引入列表页拆成查询条件区、表格区、分页区三个jsp片段。数据库字段用TEXT类型存储长文本信息时页面中不要一次性全部输出用c:out配合maxLength省略号截断显示需要完整内容时再点击查看详情。5.5 传统JSP项目打包warIDEA里打好的包部署后访问404现象在IDEA里直接右键项目选择Build Artifacts打出war包丢进Tomcat的webapps目录后启动正常但访问时404或者页面样式全丢。原因war包的目录结构不对或者项目上下文路径和浏览器里访问的路径不匹配。IDEA中Artifacts设置里如果没有配置Web Facet的war exploded结构打出来的是扁平目录而不是符合Tomcat要求的WEB-INF/classes、WEB-INF/lib标准结构。解决在IDEA的Project Structure里配置Artifacts选择Web Application: Archive确保Output directory里生成的war包解压后能看到WEB-INF目录。部署时把war包放到Tomcat的webapps目录启动后Tomcat会自动解压上下文默认是war包的文件名。如果你在IDEA里部署时没有指定Application context浏览器里访问要用http://localhost:8080/项目名/index.jsp而不是直接访问/index.jsp。这个坑在传统JSP项目打包war的场景中出现频率极高建议打包前先查看war包内的目录结构确认无误再部署。6. 交付前验证并发测试盯Servlet生命周期然后按套路打包上线6.1 用JMeter做一次带断言的并发请求验证项目能跑通和能扛住并发是两回事。我习惯在交付前用JMeter压一次登录和展览列表接口不需要复杂场景一个线程组、一个HTTP请求、一个断言就够了。jmeter -n -t LoginTest.jmx -l result.jtl -e -o report/JMeter脚本里设置线程数50、循环1次、ramp-up时间5秒并发打登录接口。断言里检查响应文本是否包含“欢迎”或“首页”的关键词如果断言失败说明登录逻辑或数据库连接池在高并发下出了问题。这个测试能直接暴露三个坑连接池最大连接数不够时会报Connection is not available, request timed out单条SQL执行太慢导致线程全部阻塞Session并发写冲突导致登录态丢失。JMeter是为了给答辩的时候拿出一份压测报告更是为了在交付前把隐患全部暴露在自己的电脑上。6.2 用Tomcat日志观测Servlet生命周期Servlet的生命周期由容器管理第一次请求时执行init()初始化之后每个请求走service()容器关闭时执行destroy()。验证它最直观的方式是在Servlet里输出日志然后观察调用顺序。Override public void init() throws ServletException { System.out.println(LoginServlet init 被调用); super.init(); } Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(LoginServlet service 处理请求: req.getRequestURI()); super.service(req, resp); } Override public void destroy() { System.out.println(LoginServlet destroy 被调用); super.destroy(); }三个方法各自打印一行日志然后连续访问两次登录接口观察Tomcat控制台init只出现一次service出现两次重启Tomcat时看到destroy。这样Servlet生命周期就不再是一个抽象概念而是一个看得到的过程。很多面试题问生命周期本质上问的就是这个观测结果——什么时候只执行一次什么时候每次执行什么时候回收资源。6.3 上线前的配置检查清单我每次交付项目前都按这个固定套路过一遍主要用一张清单来对照数据源配置里的密码是否已经改成生产库的密码c3p0-config.xml的maxPoolSize和数据库max_connections是否匹配jdbcUrl里useSSL、characterEncoding、serverTimezone三个参数是否齐全web.xml里welcome-file是否指向项目入口页面Filter过滤器中排除登录页和静态资源所有页面中的href是否都带pageContext.request.contextPath前缀打war包前查看Artifacts目录是否是标准的WEB-INF结构部署到Tomcat前确认context路径正确。其中最后一条最常见本地IDEA跑的是应用上下文/art_expo部署后访问地址不对于是404。我自己的习惯是先用JMeter压过再打war因为有些并发问题必须在多线程场景下才会出现光靠浏览器点几次看不出来。做完这个系统之后再看Spring Boot的自动配置会明白那些“约定大于配置”的背后到底解决了早期JavaWeb的哪些痛点。希望这篇笔记能帮你把这条路走通也能在答辩时讲清楚每一个选择背后的原因。本文还有配套的精品资源点击获取
网站建设高端定制企业官网