资讯详情

资讯详情

JavaWeb招聘网站实战:JSP+MySQL从环境搭建到性能优化

简介这份资源是面向计算机专业学生与JavaWeb初学者的人才招聘网站毕业设计/课程设计完整方案基于JSPMySQL的B/S架构实现可帮助读者解决从需求梳理到代码落地、文档撰写的一站式问题。压缩包共930个文件约19.32MB以gif、js、html、jar、jsp、java、class、xml、css等为主涵盖页面素材、前端脚本、JSP页面、Java源码、编译类文件与配置描述另附sql建库脚本及doc说明文档便于直接部署与二次开发。项目模块划分清晰包含前台首页、网站导航、职位信息列表、用户注册、后台登录、职位信息管理及退出后台管理等核心功能覆盖招聘网站的主要业务闭环。目前已有207人学习下载适合需要完整源码、可运行工程与配套文档的读者参考也可作为答辩演示与功能扩展的基础模板。1. 从一份 JavaWeb 招聘网站源码说起BS 架构下 JSPMySQL 到底能跑出什么很多同学第一次拿到「基于JavaWebJSPmysql的BS架构的人才招聘网站设计与实现」这类源码包第一反应是解压、丢进 IDEA、点运行然后被一堆 404、500 和数据库连接异常按在地上摩擦。这个标题背后其实是一套非常典型的 BS 架构落地路径浏览器发请求Tomcat 跑 Servlet/JSP业务逻辑用 JavaBean 和 DAO 分层数据落到 MySQL。它解决的核心问题是——如何用最传统的 JavaWeb 技术栈把「企业发职位、求职者投简历、后台管数据」这条业务闭环跑通。这套东西适合谁适合正在做课程设计、毕业设计、想补一次完整 JavaWeb 项目实战经验的在校生和初级开发者。它不适合追求高并发、微服务、前后端分离的团队因为 JSP 的服务端渲染模型在今天确实偏重。但作为理解 HTTP 请求生命周期、Session 管理、JDBC 事务、MVC 分层的练手项目它的价值依然扎实。下面我按「环境搭起来 → 数据库建起来 → 核心功能跑起来 → 坑填起来」的顺序把这条路径拆开讲清楚。2. 环境与工程结构IDEA 跑 JavaWeb 项目前必须确认的四件事2.1 JDK、Tomcat、IDEA 的版本咬合关系JavaWeb 老项目最容易翻车的地方不是代码而是版本。JSP 页面里如果用了% page languagejava %这类指令Tomcat 的版本直接决定编译行为。常见组合是 JDK 8 Tomcat 8.5/9.0 IDEA 2021 以上。JDK 17 配 Tomcat 9 也能跑但部分老项目里用的javax.servlet包名在 Tomcat 10 之后变成了jakarta.servlet一旦版本错位HttpServlet直接找不到。我一般会先确认三件事java -version输出是不是 1.8Tomcat 的lib目录下有没有servlet-api.jarIDEA 的 Project Structure 里 SDK 和 Language Level 是否一致。这三步做完能省掉后面一半的玄学报错。# 确认 JDK 版本JavaWeb 老项目优先 1.8 java -version # 确认 Tomcat 版本进入解压目录执行 cd /path/to/tomcat/bin ./version.sh # Windows 下用 version.bat逻辑说明java -version看的是运行时 JDKversion.sh看的是 Tomcat 自身版本和它依赖的 Servlet 规范版本。参数上Tomcat 8.5 对应 Servlet 3.1Tomcat 9 对应 Servlet 4.0Tomcat 10 对应 Servlet 5.0 且包名迁移到 jakarta。选错版本编译期就可能报package javax.servlet does not exist。2.2 导入源码后 IDEA 的 Facet 与 Artifact 配置源码包解压后通常是一个标准 Web 目录结构src放 Java 源码web或WebContent放 JSP、CSS、JS 和WEB-INF/web.xml。IDEA 不会自动识别这是 Web 项目需要手动加 Facet。操作步骤打开 Project Structure → Facets → 点加号选 Web → 把 Web Resource Directory 指向web目录 → 确认web.xml路径 → 再到 Artifacts 里新增 Web Application Exploded把编译输出和 lib 依赖都挂上。这一步做完Run/Debug Configurations 里才会出现 Tomcat Server 选项。!-- web/WEB-INF/web.xml 里最关键的 servlet 映射片段 -- servlet servlet-nameJobListServlet/servlet-name servlet-classcom.recruit.servlet.JobListServlet/servlet-class /servlet servlet-mapping servlet-nameJobListServlet/servlet-name url-pattern/jobList/url-pattern /servlet-mapping逻辑说明servlet-class必须和src下实际包路径完全一致大小写敏感。url-pattern是浏览器访问路径配成/jobList后JSP 里表单的 action 就要写jobList或${pageContext.request.contextPath}/jobList。参数上load-on-startup可以控制 Servlet 初始化时机招聘网站首页的职位列表 Servlet 建议设成 1避免首次访问卡顿。2.3 依赖 jar 包的放置位置与常见缺失老 JavaWeb 项目不流行 Mavenjar 包直接丢在web/WEB-INF/lib下。常见必须有的是mysql-connector-java-5.1.x.jar或 8.0.x、jstl.jar、standard.jar、commons-dbutils.jar、commons-beanutils.jar。少一个运行时就是ClassNotFoundException。提示MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver5.1 是com.mysql.jdbc.DriverURL 里还要带serverTimezone参数否则连接直接抛时区异常。2.4 项目编码统一从 JSP 到数据库的三层 UTF-8中文乱码是招聘网站项目的高频问题因为职位名称、公司简介、简历内容全是中文。要统一三层JSP 页面顶部% page contentTypetext/html;charsetUTF-8 %Servlet 里request.setCharacterEncoding(UTF-8)和response.setContentType(text/html;charsetUTF-8)数据库连接 URL 加useUnicodetruecharacterEncodingUTF-8。三层缺一层中文就可能变成问号。3. MySQL 建库建表招聘网站核心表结构与 JDBC 连接参数3.1 五张核心表的最小可用设计招聘网站的业务实体不复杂最小闭环需要用户表、企业表、职位表、简历表、投递记录表。下面给出可直接执行的建表 SQL字段做了精简但保留了外键关系。CREATE DATABASE recruit_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE recruit_db; -- 用户表求职者和企业账号共用用 role 区分 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT 0求职者 1企业 2管理员, phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 职位表企业发布的岗位 CREATE TABLE t_job ( id INT PRIMARY KEY AUTO_INCREMENT, company_id INT NOT NULL, job_name VARCHAR(100) NOT NULL, salary VARCHAR(30), city VARCHAR(50), description TEXT, publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 1在招 0下线, INDEX idx_company (company_id), INDEX idx_city (city) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 简历表 CREATE TABLE t_resume ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, real_name VARCHAR(50), education VARCHAR(30), experience TEXT, file_path VARCHAR(200), INDEX idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 投递记录表求职者投职位 CREATE TABLE t_delivery ( id INT PRIMARY KEY AUTO_INCREMENT, job_id INT NOT NULL, resume_id INT NOT NULL, delivery_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0已投递 1已查看 2邀面试, UNIQUE KEY uk_job_resume (job_id, resume_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明utf8mb4而不是utf8因为要存 emoji 和完整中文。t_delivery上的唯一索引uk_job_resume防止同一份简历重复投同一职位这是业务层必须的约束不能只靠前端校验。idx_city和idx_company是为职位列表的筛选查询准备的招聘网站最常见的查询就是「按城市查职位」和「按企业查职位」。3.2 JDBC 工具类的连接参数与连接池取舍直接DriverManager.getConnection每次新建连接在招聘网站这种读多写少的场景下性能很差。常见做法是抽一个DBUtil用c3p0或druid做连接池。如果源码包里没有连接池用最朴素的工具类也能跑但要理解它的边界。public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/recruit_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalse; private static final String USER root; private static final String PWD your_password; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动未加载, e); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } public static void close(Connection c, PreparedStatement p, ResultSet r) { try { if (r ! null) r.close(); } catch (SQLException ignored) {} try { if (p ! null) p.close(); } catch (SQLException ignored) {} try { if (c ! null) c.close(); } catch (SQLException ignored) {} } }逻辑说明serverTimezoneAsia/Shanghai是 MySQL 8.0 驱动必须的参数不写会报The server time zone value is unrecognized。useSSLfalse在本地开发环境关掉 SSL 握手减少连接开销。参数上Class.forName在 JDBC 4.0 之后理论上可省略但老项目显式加载更稳。close方法按 ResultSet → PreparedStatement → Connection 的顺序关闭顺序反了会抛异常。3.3 分页查询招聘网站职位列表的 LIMIT 写法职位列表一定要分页否则数据一多页面直接卡死。MySQL 用LIMIT offset, size配合COUNT(*)算总页数。-- 第 page 页每页 size 条page 从 1 开始 SELECT id, job_name, salary, city, publish_time FROM t_job WHERE status 1 ORDER BY publish_time DESC LIMIT #{offset}, #{size}; -- 总记录数 SELECT COUNT(*) FROM t_job WHERE status 1;逻辑说明offset (page - 1) * size。ORDER BY publish_time DESC保证最新职位在前这是招聘网站的基本体验。参数上size一般设 10 或 15offset在深分页时性能会下降数据量超过百万要考虑游标分页但课程设计级别用 LIMIT 完全够。4. 核心功能实现JSP 页面、Servlet 控制与 Session 登录态4.1 登录注册Session 存用户信息与过滤器拦截登录是招聘网站所有功能的入口。Servlet 校验账号密码后把 User 对象塞进HttpSession后续页面从 Session 取。未登录访问投递、简历页面时用 Filter 统一拦截。// LoginServlet 核心逻辑 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPwd(username, password); if (user ! null) { req.getSession().setAttribute(loginUser, user); resp.sendRedirect(req.getContextPath() /jobList); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } }逻辑说明setAttribute(loginUser, user)是整站登录态的核心JSP 里用${sessionScope.loginUser.username}取值。sendRedirect和forward的区别要清楚登录成功用重定向避免刷新重复提交失败用转发保留请求域里的错误提示。参数上密码在数据库里建议存 MD5 或 BCrypt源码包里如果存明文至少要知道这是安全隐患。// LoginFilter拦截未登录访问 public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行登录、注册、静态资源 if (uri.contains(login) || uri.contains(register) || uri.endsWith(.css) || uri.endsWith(.js)) { chain.doFilter(req, resp); return; } if (request.getSession().getAttribute(loginUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明Filter 的放行白名单必须包含登录注册页和静态资源否则 CSS 都被拦页面样式全丢。web.xml里配置filter-mapping的url-pattern为/*实现全站拦截。4.2 职位列表与详情JSP 遍历与 EL 表达式职位列表页用 JSTL 的c:forEach遍历 Servlet 放进 request 的 List详情页根据 id 查单条。% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table c:forEach items${jobList} varjob tr tda hrefjobDetail?id${job.id}${job.jobName}/a/td td${job.salary}/td td${job.city}/td /tr /c:forEach /table逻辑说明${jobList}来自request.setAttribute(jobList, list)EL 表达式自动从 page → request → session → application 逐级查找。${job.jobName}对应 JavaBean 的getJobName()命名必须符合 JavaBean 规范否则 EL 取不到值。参数上c:forEach的varStatus可以拿到循环下标做隔行变色时用得上。4.3 简历上传文件路径存储与 multipart 配置简历上传是招聘网站的特色功能Servlet 3.0 之后用MultipartConfig注解即可不需要额外 jar。WebServlet(/uploadResume) MultipartConfig(maxFileSize 5 * 1024 * 1024, maxRequestSize 10 * 1024 * 1024) public class UploadResumeServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part part req.getPart(resumeFile); String fileName System.currentTimeMillis() _ part.getSubmittedFileName(); String saveDir req.getServletContext().getRealPath(/upload); File dir new File(saveDir); if (!dir.exists()) dir.mkdirs(); part.write(saveDir File.separator fileName); // 路径入库 ResumeDao dao new ResumeDao(); dao.updateFilePath(((User) req.getSession() .getAttribute(loginUser)).getId(), upload/ fileName); resp.sendRedirect(resume.jsp); } }逻辑说明maxFileSize限制单文件 5MBmaxRequestSize限制整个请求 10MB防止大文件拖垮服务器。getSubmittedFileName()拿原始文件名前面拼时间戳避免重名覆盖。getRealPath(/upload)拿到的是部署目录下的物理路径注意 Tomcat 重启后部署目录可能被清空生产环境要存到部署目录之外。参数上part.write的路径必须是绝对路径。5. 避坑与排查JavaWeb 招聘网站跑不起来的五类高频问题5.1 现象访问任何页面都 404控制台无报错原因IDEA 的 Artifact 没配置或 Context Path 不对。源码导入后默认可能没有 Web FacetTomcat 启动的其实是空应用。解决Project Structure → Artifacts 确认有 Exploded 的 Web ApplicationOutput Layout 里WEB-INF/classes和WEB-INF/lib都在。Run Configuration 的 Deployment 标签页确认 Artifact 已添加Application context 设成/或/recruit访问时路径要对应。5.2 现象数据库连接报Access denied for user rootlocalhost原因密码错误或者 MySQL 8.0 的caching_sha2_password认证插件和 5.1 版驱动不兼容。解决先确认密码再用ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 新密码;改认证方式或者把驱动换成mysql-connector-java-8.0.x。这一步是血泪经验很多人卡在这里以为是代码问题。5.3 现象JSP 页面中文显示为乱码数据库里却是正常的原因JSP 页面本身的编码和响应头编码不一致或者 Tomcat 的server.xml里 Connector 没配 URIEncoding。解决JSP 顶部加% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %Tomcatserver.xml的 Connector 加URIEncodingUTF-8。GET 请求的中文参数尤其依赖这个配置。5.4 现象投递简历时提示重复投递但数据库里明明没有记录原因t_delivery表的唯一索引uk_job_resume生效了但业务层没做友好提示直接把 SQL 异常抛给用户。解决DAO 层捕获SQLIntegrityConstraintViolationException返回布尔值Servlet 根据返回值给前端提示「您已投递过该职位」。唯一索引是最后一道防线不是唯一校验手段。5.5 现象Tomcat 启动报ClassNotFoundException: com.mysql.cj.jdbc.Driver原因驱动 jar 没放进WEB-INF/lib或者放进了但 Artifact 的 Output Layout 没包含。解决确认 jar 在web/WEB-INF/lib下再到 Artifact 的 Output Layout 里看WEB-INF/lib目录是否列出了该 jar。IDEA 有时不会自动同步 lib 目录变化需要手动刷新或重新添加 Artifact。6. 进阶技巧用 SQL 执行日志和 EXPLAIN 验证招聘网站的查询性能项目能跑通只是第一步能不能扛住数据量是另一回事。我习惯在 MySQL 里开慢查询日志再用EXPLAIN看职位列表查询有没有走索引。-- 开启慢查询日志阈值 1 秒 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1; -- 分析职位列表查询的执行计划 EXPLAIN SELECT id, job_name, salary, city FROM t_job WHERE status 1 AND city 北京 ORDER BY publish_time DESC LIMIT 0, 10;逻辑说明EXPLAIN输出里重点看type和key两列。type是ALL说明全表扫描key是NULL说明没走索引。上面这条查询如果idx_city生效type应该是refkey是idx_city。但ORDER BY publish_time可能触发 filesort如果数据量大可以考虑建联合索引(status, city, publish_time)。参数上long_query_time设 1 秒对课程设计偏严实际可以设 2 秒。慢查询日志文件路径用SHOW VARIABLES LIKE slow_query_log_file;查。另一个实用技巧是在 JDBC URL 里加loggerLevelDEBUG仅 5.1 驱动支持能看到实际执行的 SQL 和参数排查「为什么查不到数据」时比断点还快。-- 联合索引覆盖筛选和排序 ALTER TABLE t_job ADD INDEX idx_status_city_time (status, city, publish_time);逻辑说明联合索引遵循最左前缀原则status和city是等值筛选publish_time是排序字段顺序不能乱。加完索引再用EXPLAIN对比Extra列里的Using filesort应该消失。但索引不是越多越好t_job的写操作企业发职位会因索引维护变慢招聘网站读多写少这个取舍可以接受。我自己做这类项目时养成的习惯是每加一个查询功能先用EXPLAIN看一眼执行计划再决定要不要加索引。这个习惯比事后调优省事得多。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →