在Eclipse上基于JavaWeb的毕业设计:新手入门实战与避坑指南
背景痛点:为什么第一步总是卡住
做毕设最怕“Hello World”都跑不起来。我帮学弟们远程调过最多的问题,总结下来就三句话:
- 环境配置像拆盲盒——JDK、Tomcat、MySQL、Eclipse版本只要有一个对不上,启动就报错,控制台一红屏直接心态爆炸。
- 代码组织靠复制粘贴——网上搜到的登录例子,Servlet、JSP、SQL 全写在一起,功能一多就乱成毛线,后期连自己都看不懂。
- 调试手段只有
System.out.println——断点不会打,日志不会配,出了问题只能“凭感觉”,老师一问三不知。
如果你也卡在上述任意一环,别急,下面这套“教学级”方案就是给新手量身定制的:不追新,不炫技,只求稳,能跑,能改,能答辩。
技术选型:为什么不用Spring Boot
很多同学问:“都202x年了,还用JSP+Servlet?”理由其实很简单:
- 学校机房老电脑跑得动:Eclipse 2019 + Tomcat 8 + JDK8 是机房标配,导入即用,无需下载巨量的Maven依赖。
- 答辩老师看得懂:老教授对
web.xml里配个Servlet再熟悉不过,你讲@SpringBootApplication自动装配原理,他反而觉得你“背理论”。 - 知识点全覆盖:HTTP生命周期、会话管理、JDBC、MVC分层、web安全,这些基础概念一个不少,正好写论文。
一句话:毕业设计不是生产项目,而是“把课内实验串成故事”。选最稳的方案,把故事讲圆,就能过。
核心实现:MVC 三层目录长与代码骨架
1. 在Eclipse里新建“Dynamic Web Project”
勾选“Generate web.xml deployment descriptor”,这样WEB-INF/web.xml会自动建好,省得手写。
2. 目录结构(标准MVC)
src └─main ├─java │ ├─controller // Servlet,只负责收请求、发响应 │ ├─service // 业务规则,如密码加密、验证码忽略大小写 │ ├─dao // 最原子SQL,返回POJO │ └─entity // POJO,纯数据,不含业务 └─resources └─db.properties WebContent ├─jsp // 仅展示,不写<% ├─static │ ├─css │ └─js └─WEB-INF └─lib // 手动放jar(连接池、JSTL)小提示:
WebContent是Eclipse默认的“Web根目录”,路径别乱改,否则404伺候。
3. 关键配置速览
web.xml(节选)
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>controller.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login.do</url-pattern> </servlet-mapping>db.properties
url=jdbc:mysql://localhost:3306/graduation?useSSL=false&characterEncoding=utf8 driver=com.mysql.cj.jdbc.Driver user=root password=123456 maxActive=20完整可运行示例:用户登录模块
下面代码全部基于JSP2.3 + Servlet3.1,复制即可跑,已加关键中文注释,方便写论文时直接截图。
1. 实体类(entity/User.java)
package entity; public class User { private Integer id; private String username; private String password; // 已加密,存SHA-256 // Getter/Setter 省略,自己生成 }2. DAO层(dao/UserDao.java)
package dao; import util.DBUtil; // 自己封装的连接池工具 import entity.User; import java.sql.*; public class UserDao { private static final String LOGIN_SQL = "SELECT * FROM t_user WHERE username=? AND password=?"; public User find(String username, String password) { try (Connection conn = DBUtil.getConn(); PreparedStatement ps = conn.prepareStatement(LOGIN_SQL)) { ps.setString(1, username); ps.setString(2, password); // 调用处已做SHA-256 ResultSet rs = ps.executeQuery(); if (rs.next()) { User u = new User(); u.setId(rs.getInt("id")); u.setUsername(rs.getString("username")); return u; } } catch (SQLException e) { e.printStackTrace(); // 生产环境换日志 } return null; } }3. Service层(service/UserService.java)
package service; import dao.UserDao; import entity.User; import util.SecUtil; // SHA-256+盐工具类 public class UserService { private UserDao userDao = new UserDao(); public User login(String username, String plainPwd) { String pwd = SecUtil.sha256(plainPwd); // 统一加密规则 return userDao.find(username, pwd); } }4. Servlet(controller/LoginServlet.java)
package controller; import service.UserService; import entity.User; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class LoginServlet extends HttpServlet { private UserService userService = new UserService(); @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"); User user = userService.login(username, password); if (user != null) { HttpSession session = req.getSession(); session.setAttribute("user", user); resp.sendRedirect(req.getContextPath() + "/home.jsp"); } else { req.setAttribute("msg", "账号或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }5. 登录页(login.jsp)
<%@ page contentType="text/html;charset=UTF-8" %> <html> <head><title>登录</title></head> <body> <form action="login.do" method="post"> 用户名:<input name="username"><br> 密 码:<input type="password" name="password"><br> <button type="submit">登录</button> </form> <p style="color:red">${msg}</p> </body> </html>注意:表单
action与web.xml中url-pattern保持一致,区分大小写。
6. 连接池工具(util/DBUtil.java)
package util; import com.alibaba.druid.pool.DruidDataSource; import java.io.InputStream; import java.sql.Connection; import java.util.Properties; public class DBUtil { private static DruidDataSource ds; static { try (InputStream in = DBUtil.class.getClassLoader() .getResourceAsStream("db.properties")) { Properties p = new Properties(); p.load(in); ds = new DruidDataSource(); ds.setUrl(p.getProperty("url")); ds.setDriverClassName(p.getProperty("driver")); ds.setUsername(p.getProperty("user")); ds.setPassword(p.getProperty("password")); ds.setMaxActive(Integer.parseInt(p.getProperty("maxActive"))); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConn() throws SQLException { return ds.getConnection(); } }Druid连接池体积小,配置简单,毕设级别足够。
性能与安全:别让“基础”变成“漏洞”
- 连接池:上面已用Druid,默认带监控页面
/druid/index.html,开不开都行,记得加账号别公网裸奔。 - SQL注入:一律
PreparedStatement,千万别拼字符串。 - 会话管理:登录成功把
User对象放session,后续用Filter检查session.getAttribute("user")==null就踢回登录页。 - 密码存储:明文=0分,MD5=及格,SHA-256+随机盐=80分,想冲优再加bcrypt。
生产环境避坑Top5
中文乱码
- JSP页头加
<%@ page contentType="text/html;charset=UTF-8" %> - Eclipse工作区、数据库、Tomcat URIEncoding="UTF-8"三处统一。
- JSP页头加
404/500资源路径
- 静态文件放
WebContent/static,引用用${pageContext.request.contextPath}/static/xxx.css - 拒绝写死路径
../css/xx.css。
- 静态文件放
热部署失效
- 双击Tomcat → Publishing → 选“Automatically publish when resources change”
- 改Java代码后仍要重启,JSP可即时生效,别傻傻等。
端口被占
netstat -ano | findstr 8080找到PID,任务管理器结束进程;或改server.xml里Connector port。
依赖冲突
- 手动放
WEB-INF/lib时,只保留一个版本的mysql-connector-j,多了会ClassCastException。
- 手动放
下一步:动手重构 & 拓展
- 把
LoginServlet拆出公共基类BaseServlet,用反射统一分发method=xxx,省得每功能一个类。 - 写个
AuthFilter,对/admin/*统一鉴权,把权限逻辑从Servlet里清出去。 - 把Druid监控账号写进论文“系统安全”章节,截图贴配置,字数+说服力双丰收。
- 有余力再升级:把JSP换成FreeMarker,DAO换成MyBatis,但原项目结构不动,让导师一眼看出“渐进式改进”,答辩更稳。
照着上面的骨架把代码跑通,再把自己的业务模块往里填,基本就能交差。别追求完美,先让项目“可运行、可演示、可讲解”,再逐步重构。祝你一次编译通过,答辩现场不被问倒!