1. 项目缘起与整体设计思路
1.1 这个课程设计到底要做什么
JavaWeb课程设计几乎是每个计算机相关专业学生绕不开的一道坎。题目看起来简单——“制作一个页面展示和管理自己和家乡的信息”,但真正动手做的时候,很多人才发现里面涉及的知识点远比想象中密集。这个项目的本质,是用Java技术栈搭建一个具备增删改查能力的动态信息管理站点,前端负责展示,后端负责逻辑处理,数据库负责数据持久化。
具体来说,这个项目需要实现的核心功能包括:一个展示页面,用来呈现个人信息(姓名、专业、年级、兴趣爱好等)和家乡信息(地理位置、特色美食、风景名胜、民俗文化等);一个管理后台,允许对上述信息进行新增、修改、删除操作;以及前后端之间的数据交互逻辑。听起来像是“做个网页”,实际上它串联了HTML、CSS、JavaScript、Servlet、JSP、JDBC、数据库设计、会话管理、请求转发与重定向等一整套JavaWeb核心知识。
适合做这个项目的人,是已经学完Java基础、正在学习或刚学完Servlet和JSP的在校学生,或者想通过一个完整项目把零散知识串起来的自学者。如果你连HttpServletRequest和HttpServletResponse都还没接触过,建议先把基础过一遍再上手,否则会做得很痛苦。
1.2 为什么选择JSP+Servlet而不是直接上框架
这是我在带课程设计时被问得最多的问题:“都什么年代了还用JSP?直接用Spring Boot不香吗?”这个问题值得认真回答。
课程设计的核心目的不是让你快速交付一个产品,而是让你理解Web开发的底层运作机制。Spring Boot确实能让你少写很多配置,但它的高度封装会掩盖很多关键细节——请求是怎么被处理的、会话是怎么维持的、数据库连接是怎么管理的。用JSP+Servlet做一遍,这些环节你都得亲手处理,做完之后再看Spring Boot,你会明白它到底帮你做了什么。
从技术选型角度,JSP+Servlet方案有几个实际优势。第一,学习成本可控,不需要理解依赖注入、AOP、自动配置这些概念;第二,调试直观,出了问题堆栈信息清晰,不像框架那样报错层层嵌套;第三,部署简单,一个Tomcat加上项目war包就能跑起来,不需要额外安装Maven以外的构建工具。当然,JSP+Servlet也有明显的缺点,比如代码容易写得混乱、视图和逻辑容易耦合,但这些恰恰是你可以通过良好的项目结构来规避的,而规避的过程本身就是一种学习。
我的建议是:课程设计用JSP+Servlet,把MVC分层思想贯彻进去,做完之后再用Spring Boot重写一遍同样的功能,对比两者的差异。这样一轮下来,你对JavaWeb的理解会比只做一个Spring Boot项目深刻得多。
1.3 项目整体架构怎么划分
一个结构清晰的JavaWeb项目,应该遵循MVC(Model-View-Controller)分层原则。具体到这个项目,我建议的包结构是这样的:
com.homepage.entity:存放实体类,比如PersonalInfo、HometownInfo、User等,每个类对应数据库中的一张表。com.homepage.dao:数据访问层,封装所有JDBC操作,提供增删改查方法。这一层只负责和数据库打交道,不关心业务逻辑。com.homepage.service:业务逻辑层,处理具体的业务规则,比如“修改信息前先检查是否登录”“删除前确认记录存在”等。com.homepage.servlet:控制层,接收前端请求,调用Service层处理,然后转发或重定向到相应页面。com.homepage.util:工具类,比如数据库连接工具DBUtil、字符串处理工具等。com.homepage.filter:过滤器,用于处理字符编码、登录验证等全局逻辑。
前端页面放在WebContent(或webapp)目录下,JSP文件按功能分目录存放,比如/admin/放后台管理页面,/user/放前台展示页面。静态资源(CSS、JS、图片)统一放在/static/目录下。
这种分层方式的好处是职责清晰。举个例子,如果有一天你要把数据库从MySQL换成其他数据库,只需要改DAO层的实现,Service层和Servlet层几乎不用动。同样,如果前端要改成前后端分离的架构,也只需要调整Servlet层的返回格式,业务逻辑不受影响。
注意:很多同学做课程设计时喜欢把JDBC代码直接写在JSP里,这是大忌。JSP里写Java代码不仅难以维护,而且会让页面逻辑和业务逻辑纠缠不清,后期改一个功能要翻好几个文件。坚持分层,哪怕一开始觉得麻烦,后面会感谢自己。
2. 核心功能模块的细节拆解
2.1 数据库设计:两张核心表怎么建
数据库设计是整个项目的地基,地基没打好,后面写代码会处处别扭。这个项目至少需要两张核心表:一张存个人信息,一张存家乡信息。如果要做登录功能,还需要一张用户表。
先看个人信息表personal_info的设计:
CREATE TABLE personal_info ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT '姓名', major VARCHAR(100) COMMENT '专业', grade VARCHAR(20) COMMENT '年级', hobby VARCHAR(255) COMMENT '兴趣爱好', intro TEXT COMMENT '个人简介', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;家乡信息表hometown_info的设计:
CREATE TABLE hometown_info ( id INT PRIMARY KEY AUTO_INCREMENT, location VARCHAR(100) NOT NULL COMMENT '地理位置', food VARCHAR(255) COMMENT '特色美食', scenery VARCHAR(255) COMMENT '风景名胜', culture TEXT COMMENT '民俗文化', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;用户表user的设计:
CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有几个设计决策需要解释。第一,为什么用utf8mb4而不是utf8?因为utf8在MySQL中最多只支持3字节的字符,遇到emoji或者某些特殊汉字会报错,utf8mb4才是真正的UTF-8编码。第二,为什么加update_time字段?这是为了方便追踪记录的最后修改时间,在管理后台显示“最后更新时间”会让页面看起来更专业。第三,密码字段长度设为100,是因为如果后续要做密码加密(比如MD5或SHA-256),加密后的字符串会比原文长很多。
实操心得:建表时一定要加
ENGINE=InnoDB,因为InnoDB支持事务和外键,而MyISAM不支持。虽然这个项目可能用不到事务,但养成习惯没坏处。另外,字段的COMMENT一定要写,过一个月回头看表结构,没有注释你根本想不起来某个字段是干嘛的。
2.2 数据库连接工具类的封装
JDBC操作的第一步是获取数据库连接。如果每次操作都写一遍DriverManager.getConnection(),代码会非常冗余,而且数据库配置信息散落在各处,改起来容易漏。正确的做法是封装一个DBUtil工具类。
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/homepage_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4"; private static final String USERNAME = "root"; private static final String PASSWORD = "your_password"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new RuntimeException("MySQL驱动加载失败", e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USERNAME, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 依次关闭资源,注意关闭顺序和异常处理 } }这里有几个关键点。第一,Class.forName()放在静态代码块中,保证驱动只加载一次。第二,URL中加了serverTimezone=Asia/Shanghai,这是MySQL 8.0之后的必须参数,不加会报时区错误。第三,characterEncoding=utf8mb4确保中文不会乱码。第四,关闭资源的方法要处理null判断和异常捕获,避免因为一个资源关闭失败导致后面的资源无法关闭。
如果你想让项目更规范,可以把数据库配置信息抽到db.properties文件中,用Properties类读取。这样部署到不同环境时只需要改配置文件,不用重新编译代码。
2.3 实体类与DAO层的对应关系
实体类是数据库表在Java中的映射。以PersonalInfo为例:
public class PersonalInfo { private Integer id; private String name; private String major; private String grade; private String hobby; private String intro; private Date updateTime; // 省略getter和setter方法 }实体类的字段名建议和数据库字段名保持一致(或者用驼峰命名对应下划线命名),这样在DAO层做结果集映射时不容易出错。字段类型的选择也要注意:数据库的INT对应Integer而不是int,因为Integer可以为null,能更好地处理数据库中的NULL值;数据库的DATETIME对应java.util.Date或java.sql.Timestamp。
DAO层为每个实体类提供一个对应的DAO类,比如PersonalInfoDao,里面包含以下方法:
findAll():查询所有个人信息记录findById(int id):根据ID查询单条记录insert(PersonalInfo info):新增记录update(PersonalInfo info):修改记录delete(int id):删除记录
每个方法的实现都遵循“获取连接→创建PreparedStatement→设置参数→执行SQL→处理结果→关闭资源”的流程。这里一定要用PreparedStatement而不是Statement,因为PreparedStatement能防止SQL注入,而且预编译机制能提升执行效率。
public boolean update(PersonalInfo info) { String sql = "UPDATE personal_info SET name=?, major=?, grade=?, hobby=?, intro=? WHERE id=?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, info.getName()); ps.setString(2, info.getMajor()); ps.setString(3, info.getGrade()); ps.setString(4, info.getHobby()); ps.setString(5, info.getIntro()); ps.setInt(6, info.getId()); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } }这里用了Java 7的try-with-resources语法,能自动关闭Connection和PreparedStatement,代码更简洁。注意ResultSet也需要关闭,但在executeUpdate()场景下不需要处理结果集。
2.4 前端页面的结构设计
前端部分虽然不需要像专业前端那样精细,但基本的布局和交互还是要有的。展示页面建议分为两个区域:个人信息区和家乡信息区。个人信息区用卡片式布局展示姓名、专业、年级、兴趣爱好等;家乡信息区可以用图文混排的方式展示地理位置、美食、风景和文化。
页面结构用HTML5语义化标签:
<div class="container"> <header> <h1>个人与家乡信息展示</h1> </header> <section class="personal-info"> <h2>个人信息</h2> <div class="info-card"> <p><strong>姓名:</strong>${personalInfo.name}</p> <p><strong>专业:</strong>${personalInfo.major}</p> <!-- 其他字段 --> </div> </section> <section class="hometown-info"> <h2>家乡信息</h2> <div class="info-card"> <p><strong>地理位置:</strong>${hometownInfo.location}</p> <!-- 其他字段 --> </div> </section> </div>JSP页面中用EL表达式${}来显示数据,比传统的<%= %>脚本片段更简洁,也更符合MVC的设计理念。如果EL表达式不生效,检查一下是否引入了JSTL标签库,以及web.xml中的版本声明是否正确。
CSS部分不需要太复杂,重点是布局清晰、配色舒适。可以用Flexbox做响应式布局,保证在手机和电脑上都能正常显示。颜色建议用柔和的色调,比如浅灰背景配白色卡片,文字用深灰色而不是纯黑色,看起来更舒服。
注意事项:JSP页面中的中文乱码问题几乎每个人都会遇到。解决办法是在每个JSP页面顶部加上
<%@ page contentType="text/html;charset=UTF-8" language="java" %>,同时在Servlet中设置request.setCharacterEncoding("UTF-8")和response.setContentType("text/html;charset=UTF-8")。如果用了POST请求还有乱码,检查一下Tomcat的server.xml中Connector的URIEncoding属性。
3. 完整实操流程与关键环节实现
3.1 开发环境搭建与项目初始化
动手写代码之前,先把环境搭好。需要准备的东西包括:JDK(建议1.8或以上)、Tomcat(建议8.5或9.0)、MySQL(建议5.7或8.0)、IDE(Eclipse或IntelliJ IDEA都行)、MySQL驱动jar包(mysql-connector-java)。
在IDE中创建项目时,选择“Dynamic Web Project”(Eclipse)或“Java Enterprise”项目(IDEA)。创建完成后,把MySQL驱动jar包放到WEB-INF/lib目录下,这样Tomcat运行时能自动加载。如果你用Maven管理依赖,在pom.xml中添加:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>servlet-api的scope设为provided,因为Tomcat本身已经包含了这个jar包,打包时不需要重复包含,否则会冲突。
项目创建好后,先写一个简单的测试Servlet,部署到Tomcat上访问一下,确认环境没问题再继续。这一步看似多余,但能帮你提前发现端口占用、版本不兼容等问题,避免写到一半才发现环境有问题。
3.2 展示页面的数据查询与渲染流程
展示页面的数据流向是这样的:用户访问展示页面 → Servlet接收请求 → 调用Service层 → Service调用DAO层查询数据库 → 返回实体对象 → Servlet将对象存入request域 → 转发到JSP页面 → JSP用EL表达式渲染数据。
对应的Servlet代码:
@WebServlet("/showInfo") public class ShowInfoServlet extends HttpServlet { private PersonalInfoService personalService = new PersonalInfoService(); private HometownInfoService hometownService = new HometownInfoService(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PersonalInfo personalInfo = personalService.getLatest(); HometownInfo hometownInfo = hometownService.getLatest(); request.setAttribute("personalInfo", personalInfo); request.setAttribute("hometownInfo", hometownInfo); request.getRequestDispatcher("/user/show.jsp").forward(request, response); } }这里用getLatest()而不是findAll(),是因为展示页面通常只需要显示最新的一条记录。如果数据库中有多条记录,可以在Service层做逻辑判断,比如按update_time倒序取第一条。
JSP页面中渲染数据:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>个人与家乡信息</title> <link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/style.css"> </head> <body> <div class="container"> <c:if test="${not empty personalInfo}"> <section class="personal-info"> <h2>个人信息</h2> <p>姓名:${personalInfo.name}</p> <p>专业:${personalInfo.major}</p> <p>年级:${personalInfo.grade}</p> <p>兴趣爱好:${personalInfo.hobby}</p> <p>个人简介:${personalInfo.intro}</p> </section> </c:if> <c:if test="${empty personalInfo}"> <p>暂无个人信息,请先到后台添加。</p> </c:if> </div> </body> </html>用<c:if>做空值判断是必要的,否则如果数据库中没有数据,页面会显示一片空白或者报错。${pageContext.request.contextPath}用来获取项目的上下文路径,这样即使项目部署的路径变了,静态资源的引用也不会失效。
3.3 后台管理功能的增删改查实现
后台管理是这个项目的重头戏,也是最能体现技术含量的部分。管理功能包括:查看所有记录列表、新增记录、编辑记录、删除记录。
列表页面的实现比较简单,查询所有记录后转发到JSP,用<c:forEach>遍历显示:
<table> <thead> <tr> <th>ID</th> <th>姓名</th> <th>专业</th> <th>操作</th> </tr> </thead> <tbody> <c:forEach items="${personalInfoList}" var="info"> <tr> <td>${info.id}</td> <td>${info.name}</td> <td>${info.major}</td> <td> <a href="${pageContext.request.contextPath}/admin/editPersonal?id=${info.id}">编辑</a> <a href="javascript:void(0)" onclick="confirmDelete(${info.id})">删除</a> </td> </tr> </c:forEach> </tbody> </table>删除操作建议用JavaScript做二次确认,避免误删:
function confirmDelete(id) { if (confirm("确定要删除这条记录吗?删除后不可恢复。")) { window.location.href = "${pageContext.request.contextPath}/admin/deletePersonal?id=" + id; } }新增和编辑可以共用一个表单页面。新增时表单为空,编辑时表单回填原有数据。判断依据是请求中是否携带了id参数。Servlet中这样处理:
@WebServlet("/admin/editPersonal") public class EditPersonalServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idStr = request.getParameter("id"); if (idStr != null && !idStr.isEmpty()) { int id = Integer.parseInt(idStr); PersonalInfo info = personalService.findById(id); request.setAttribute("personalInfo", info); } request.getRequestDispatcher("/admin/personalForm.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 收集表单参数,构建PersonalInfo对象 // 判断id是否为空,决定是insert还是update // 操作完成后重定向到列表页面 } }这里有一个重要的设计原则:查询用转发,修改用重定向。因为如果修改操作后直接转发到列表页面,用户刷新页面时会重复提交表单,导致数据重复插入。用重定向可以避免这个问题,浏览器地址栏会变成列表页面的URL,刷新也不会重复提交。
3.4 登录验证与Session管理
后台管理功能不应该让所有人都能访问,所以需要加一个简单的登录验证。用户表里存一个管理员账号,登录成功后把用户信息存入Session,后续访问后台页面时检查Session中是否有用户信息。
登录Servlet的核心逻辑:
@WebServlet("/login") public class LoginServlet extends HttpServlet { @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"); User user = userService.login(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("currentUser", user); session.setMaxInactiveInterval(30 * 60); // 30分钟超时 response.sendRedirect(request.getContextPath() + "/admin/list"); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } }然后用一个Filter拦截所有/admin/*的请求,检查Session:
@WebFilter("/admin/*") public class AuthFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); if (session == null || session.getAttribute("currentUser") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }request.getSession(false)表示如果Session不存在就返回null,而不是新建一个。这个细节很重要,否则每个未登录的请求都会创建一个新Session,浪费服务器资源。
实操心得:密码存储千万不要用明文。哪怕课程设计不要求,也建议用MD5或SHA-256做一次哈希。Java中可以用
MessageDigest类实现:
public static String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }这样即使数据库被看到,密码也不是明文。当然MD5本身也不够安全,但比明文强得多,而且实现简单,适合课程设计的场景。
4. 常见问题排查与避坑经验
4.1 中文乱码问题的完整解决方案
中文乱码是JavaWeb开发中最常见的问题,没有之一。乱码可能出现在三个环节:请求参数乱码、响应内容乱码、数据库存储乱码。每个环节的解决方案不同,需要逐一排查。
请求参数乱码:POST请求的参数乱码,用request.setCharacterEncoding("UTF-8")解决,这行代码必须放在获取任何参数之前。GET请求的参数乱码,需要修改Tomcat的server.xml,在Connector标签中添加URIEncoding="UTF-8"。注意Tomcat 8.0之后默认就是UTF-8,但如果你用的老版本,这个配置是必须的。
响应内容乱码:用response.setContentType("text/html;charset=UTF-8")解决。如果页面还是乱码,检查JSP顶部的page指令是否设置了charset=UTF-8。
数据库存储乱码:检查数据库和表的字符集是否为utf8mb4,连接URL中是否加了characterEncoding=utf8mb4。如果数据已经乱码了,需要清空表重新插入。
排查乱码问题的思路是:先确定乱码发生在哪个环节。可以在Servlet中打印接收到的参数值,如果打印出来就是乱码,说明是请求环节的问题;如果打印正常但页面显示乱码,说明是响应环节的问题;如果页面显示正常但数据库里是乱码,说明是数据库环节的问题。
4.2 数据库连接失败的排查步骤
数据库连接失败通常有以下几种原因,按排查优先级排列:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| ClassNotFoundException | 驱动jar包未引入 | 将mysql-connector-java放入WEB-INF/lib |
| Access denied for user | 用户名或密码错误 | 检查DBUtil中的用户名密码 |
| Unknown database | 数据库不存在 | 创建对应的数据库 |
| Communications link failure | MySQL服务未启动 | 启动MySQL服务 |
| The server time zone value... | 时区未设置 | URL中添加serverTimezone参数 |
| Public Key Retrieval is not allowed | MySQL 8.0安全设置 | URL中添加allowPublicKeyRetrieval=true |
我遇到过最坑的一个问题是:项目在本地跑得好好的,换一台电脑就报连接失败。排查了半天发现是那台电脑的MySQL端口不是默认的3306,而是3307。所以排查连接问题时,先确认端口号、用户名、密码、数据库名这四个基本信息是否正确。
4.3 JSP页面报错的常见原因
JSP报错信息往往很长,但核心信息通常在最后几行。常见的JSP报错包括:
500错误,提示“Unable to compile class for JSP”:通常是JSP中的Java代码有语法错误,比如少了一个分号、括号不匹配等。仔细看错误信息中提示的行号,去对应位置检查。
404错误,提示“The requested resource is not available”:检查Servlet的@WebServlet注解路径是否正确,或者web.xml中的url-pattern是否匹配。另外注意项目部署路径,如果项目名是homepage,访问路径应该是http://localhost:8080/homepage/showInfo而不是http://localhost:8080/showInfo。
EL表达式不生效,页面直接显示${personalInfo.name}:这是因为EL表达式被当成了普通文本。解决办法是在JSP顶部加上<%@ page isELIgnored="false" %>,或者检查web.xml的版本声明是否为Servlet 2.4以上。
JSTL标签报错“The absolute uri cannot be resolved”:缺少JSTL的jar包,需要引入jstl.jar和standard.jar(或者Maven中的jstl依赖)。
4.4 部署到Tomcat时的注意事项
项目在IDE中运行正常,部署到独立的Tomcat上却出问题,这种情况也很常见。主要检查以下几点:
第一,确认war包中包含了所有依赖的jar。IDE中运行时,IDE会自动把依赖加到classpath中,但打包时可能遗漏。检查WEB-INF/lib目录下是否有所有需要的jar包。
第二,确认Tomcat的版本和Servlet版本匹配。如果你在web.xml中声明了Servlet 4.0,但Tomcat 7只支持到Servlet 3.0,就会报错。要么升级Tomcat,要么降低web.xml的版本声明。
第三,确认数据库配置是否正确。部署到服务器上时,数据库可能不在本地,需要修改连接URL中的IP地址。
第四,查看Tomcat的日志文件。日志在logs目录下,catalina.out或localhost.log中会有详细的错误信息。很多人遇到问题只看浏览器上的报错,其实Tomcat日志里的信息更详细。
避坑技巧:部署前先在本地用
mvn clean package(如果用了Maven)打一个war包,解压检查WEB-INF/lib和WEB-INF/classes目录是否完整。这个习惯能帮你提前发现很多部署问题。
4.5 代码组织与后期维护的建议
课程设计做完之后,很多人就不管了。但如果你想让这个项目成为简历上的亮点,或者作为后续项目的模板,代码组织就很重要。
首先,命名要规范。类名用大驼峰(PersonalInfoService),方法名和变量名用小驼峰(findById、userName),常量全大写加下划线(MAX_PAGE_SIZE)。不要用拼音命名,也不要用aaa、bbb这种无意义的名称。
其次,注释要写关键位置。不需要每行都写注释,但类的作用、方法的参数和返回值、复杂的业务逻辑一定要有注释。特别是DAO层的SQL语句,注释一下每个参数对应什么条件,后期改起来会方便很多。
第三,把可配置的信息抽出来。数据库连接信息、每页显示的记录数、Session超时时间等,都应该放在配置文件或常量类中,而不是硬编码在代码里。
第四,养成用Git的习惯。哪怕只有自己一个人开发,用Git管理代码也能让你随时回退到之前的版本。每次完成一个功能就提交一次,提交信息写清楚做了什么。
这个项目虽然简单,但如果你能把上面这些细节都做到位,它就不只是一个课程设计,而是一个能体现你工程素养的作品。我在实际带项目时发现,那些在课程设计阶段就注重代码质量的同学,到了毕业设计和工作阶段,上手速度明显快很多。