简介:本资源是一份面向Java Web初学者与高校学生的期末综合实训项目,完整实现基于Servlet+JSP+JDBC的教务管理系统,覆盖MVC分层架构、前后端交互及CRUD核心业务逻辑。资源包共74个文件,含20个Java后端类(含Servlet、DAO、工具类)、15个JSP页面(登录、注册、成绩管理等模块)、13个XML配置文件(web.xml、pom.xml等)、6个CSS与5个JS脚本(Bootstrap+jQuery实现响应式界面与前端校验),以及SQL建表语句、数据库设计说明和配套Word版项目说明书,压缩包大小为9.26MB。已有11207人学习下载,内容结构清晰、功能闭环——学生端支持注册登录、成绩查看与导出PDF、个人信息维护;教师端涵盖学生/成绩全生命周期管理,并集成验证码生成、头像上传、密码找回等实用功能,开箱即用,适合作为课程设计参考、答辩演示或二次开发基础框架。
1. 项目概述:一个“完成度100%”的Java Web项目意味着什么?
又到了期末季,相信不少计算机相关专业的同学,尤其是正在学习Java Web开发方向的,都在为那个“期末大作业”发愁。当你在各种论坛、社区或者同学群里看到“Java Web期末大作业,完成度100%附带文档”这样的标题时,心里是不是既羡慕又有点没底?作为一个经历过无数次项目评审和带过不少新人的老码农,我想告诉你,一个真正能拿高分的“100%完成度”项目,远不止是把代码跑通那么简单。它更像是一个微缩的企业级应用开发全流程演练,从需求理解、技术选型、编码实现、测试调试到最终的项目打包和文档撰写,每一个环节都藏着大学问。
这个标题里的“Java Web”,通常指的是使用Java技术栈来构建浏览器/服务器(B/S)架构的应用程序。核心就是用户通过浏览器访问,请求发送到后端的Java服务器,服务器处理业务逻辑、访问数据库,最后生成动态的HTML页面返回给浏览器。实现它,你绕不开Servlet、JSP这些经典技术,或者现在更流行的Spring Boot框架。而“完成度100%”这个说法,在老师眼里,绝不仅仅是功能列表上的对勾。它至少意味着:核心业务功能流程完整且可验证、用户界面基本可用、数据能正确持久化、没有影响使用的致命Bug,以及最重要的——有一份清晰、规范、能体现你设计思路的配套文档。
接下来,我就以一个过来人的视角,帮你拆解一下,要做出这样一个“标杆级”的期末大作业,你需要关注哪些核心维度,以及如何避开那些新手最容易踩的坑。我们会从项目选题与设计开始,一直聊到代码实现、调试和文档编写,目标就是让你不仅能交差,更能通过这个过程真正掌握企业级Web开发的基本套路。
2. 项目选题与整体设计思路拆解
选题是第一步,也是决定你后续开发体验和最终得分上限的关键。一个好的选题应该是“麻雀虽小,五脏俱全”。
2.1 如何选择一个“恰到好处”的题目?
千万别贪大求全。我曾经见过有同学雄心勃勃要做“仿淘宝电商系统”,结果光商品SKU管理、购物车状态保持、分布式事务这些概念就足以让项目烂尾。对于期末大作业,我强烈推荐选择业务模型清晰、功能模块在4-6个之间的题目。
几个经典且安全的选题方向:
- 图书管理系统:几乎是教科书般的案例。涉及用户(管理员/普通读者)、图书(增删改查、分类)、借阅/归还(核心业务流程)、查询统计。业务逻辑直接,数据库表设计(用户表、图书表、借阅记录表)也经典。
- 学生选课/成绩管理系统:贴合校园场景。功能包括学生信息管理、课程库管理、选课逻辑(时间冲突、容量检查)、成绩录入与查询。能很好地练习事务处理(比如选课成功后要减少课程余量)和复杂查询。
- 简易论坛/Blog系统:侧重内容展示与交互。包括用户发帖/回帖、文章分类、内容展示、简单的权限控制(如只有作者可编辑删除)。适合练习前端页面设计和用户会话(Session)管理。
- 个人事务管理(Todo List)或简易OA:功能聚焦,可以做得很有特色。比如Todo List可以增加优先级、标签、截止日期提醒;简易OA可以包含请假流程、公告发布等。
注意:无论选哪个,一定要在开始编码前,用纸笔或工具(如XMind)画出核心的业务流程图和功能模块图。这能帮你理清思路,也是后续文档的重要组成部分。
2.2 技术选型:经典派 vs 现代派?
技术选型没有绝对的对错,但需要符合课程要求和你自身的掌握程度。通常有两种路径:
路径一:经典Servlet/JSP + JDBC + MySQL这是最基础、最纯粹的技术栈,能让你深刻理解Web请求的生命周期(从Tomcat容器到Servlet的doGet/doPost)。
- 优势:概念清晰,对底层理解有帮助,几乎所有老师都认可。部署简单,一个WAR包扔到Tomcat的
webapps目录下就行。 - 劣势:需要手动处理很多琐事,如数据库连接池管理、Servlet映射配置(
web.xml)、JSP页面的Java脚本与HTML混合(容易写得混乱)。 - 适合人群:课程大纲明确要求使用此技术栈,或者你想夯实Web开发基础原理的同学。
路径二:Spring Boot + MyBatis/Spring Data JPA + Thymeleaf/前后端分离这是目前企业开发的主流选择,框架帮你处理了大量样板代码。
- 优势:开发效率极高。Spring Boot自动配置,内嵌Tomcat,一键启动。MyBatis或JPA让数据库操作变得简单。Thymeleaf模板引擎比JSP更现代优雅。如果采用前后端分离(后端提供RESTful API,前端用Vue/React),项目结构更清晰,也更具前瞻性。
- 劣势:框架本身有一定学习成本,如果只是照搬代码而不理解其原理(如IoC、AOP),容易成为“配置工程师”。
- 适合人群:希望项目更贴近现代企业开发,或者有额外精力想挑战自己的同学。
我的建议:如果你的课程没有硬性规定,优先选择Spring Boot。因为它能让你更专注于业务逻辑的实现,而不是陷入配置的泥潭。一个用Spring Boot搭建的、结构清晰的项目,在答辩时也更能体现你的学习主动性。
2.3 开发环境与工具准备
工欲善其事,必先利其器。一个高效的开发环境能事半功倍。
- JDK:统一版本!建议使用JDK 8或JDK 11(LTS长期支持版)。小组协作时,必须确保所有人的JDK版本一致,避免因版本差异导致的诡异问题。
- IDE:IntelliJ IDEA Ultimate(学生可免费申请)或 Eclipse for Enterprise Java Developers。IDEA在智能提示、Spring Boot支持方面更胜一筹。
- 构建工具:Maven或Gradle。用于管理项目依赖(jar包)。Maven的
pom.xml配置文件是项目的“依赖清单”。 - 数据库:MySQL 5.7或8.0。安装后,建议使用图形化管理工具如Navicat、DBeaver或IDEA自带的数据库工具,方便查看和操作数据。
- 版本控制:必须使用Git!在GitHub、Gitee或GitLab上创建私有仓库。每完成一个功能或修复一个Bug,就做一次提交(Commit)。这不仅是好习惯,当代码改乱时,它能救你的命。这也是你项目过程管理的有力证明。
- 浏览器开发者工具:Chrome或Edge的F12开发者工具,用于调试前端JavaScript、查看网络请求、分析页面元素,是排查前后端交互问题的利器。
3. 项目核心模块实现与编码要点
假设我们选定“图书管理系统”作为示例,采用Spring Boot + MyBatis + Thymeleaf技术栈。我们来拆解几个核心模块的实现。
3.1 项目骨架搭建与配置
首先,通过Spring Initializr(可在IDEA中直接使用)生成项目基础结构。依赖选择:Spring Web(Web框架)、MyBatis Framework(数据库框架)、MySQL Driver(数据库驱动)、Thymeleaf(模板引擎)、Lombok(简化Java Bean代码)。
关键的配置文件application.yml(或application.properties)需要正确设置:
server: port: 8080 # 服务端口 spring: datasource: url: jdbc:mysql://localhost:3306/library_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false # 开发时关闭缓存,修改页面后立即生效 mybatis: mapper-locations: classpath:mapper/*.xml # MyBatis的SQL映射文件位置 configuration: map-underscore-to-camel-case: true # 自动将数据库的user_name映射到Java的userName属性实操心得:
serverTimezone=Asia/Shanghai这个参数非常重要,能避免因数据库时区设置导致的日期时间字段插入/查询异常。这是新手常踩的一个坑。
3.2 数据库设计与实体类映射
根据业务,我们设计三张核心表:
book(图书表):id(主键),isbn,name,author,press(出版社),price,status(状态:0-可借,1-已借出),location(馆藏位置)等。user(用户表):id,username,password(存储加密后的),role(角色:admin-管理员,user-普通读者),phone,email等。borrow_record(借阅记录表):id,user_id,book_id,borrow_time,due_time(应还时间),return_time(实际归还时间),status(记录状态)等。
在Java中,使用Lombok注解创建对应的实体类(Entity):
@Data // Lombok注解,自动生成getter, setter, toString等方法 @NoArgsConstructor @AllArgsConstructor public class Book { private Long id; private String isbn; private String name; private String author; private String press; private BigDecimal price; private Integer status; // 使用状态码,便于扩展 private String location; // 省略 getter/setter (由Lombok生成) }注意事项:实体类的属性名应与数据库表字段名遵循“下划线转驼峰”的约定,并利用MyBatis的
map-underscore-to-camel-case配置自动映射。status这类字段使用Integer类型比String更好,便于进行状态判断(如if(book.getStatus() == 0))。
3.3 数据访问层(DAO/Mapper)与业务逻辑层(Service)
这是后端核心。我们采用经典的“Controller-Service-Mapper”三层架构。
1. Mapper接口与XML映射文件:首先定义BookMapper.java接口:
@Mapper // 关键注解,MyBatis扫描识别 public interface BookMapper { int insert(Book book); int updateById(Book book); int deleteById(Long id); Book selectById(Long id); List<Book> selectAll(); // 动态条件查询示例 List<Book> selectByCondition(@Param("name") String name, @Param("author") String author, @Param("status") Integer status); }然后在resources/mapper/目录下创建BookMapper.xml,编写具体的SQL:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.yourpackage.mapper.BookMapper"> <sql id="Base_Column_List">id, isbn, name, author, press, price, status, location</sql> <select id="selectByCondition" resultType="com.yourpackage.entity.Book"> SELECT <include refid="Base_Column_List"/> FROM book <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="author != null and author != ''"> AND author LIKE CONCAT('%', #{author}, '%') </if> <if test="status != null"> AND status = #{status} </if> </where> </select> </mapper>核心技巧:使用
<where>标签和<if>标签构建动态SQL,可以优雅地处理多条件查询,避免SQL拼接错误和WHERE关键字多余的问题。#{}是MyBatis的参数占位符,能有效防止SQL注入。
2. Service层实现业务逻辑:BookService接口定义业务方法,BookServiceImpl实现类注入BookMapper并编写逻辑。
@Service public class BookServiceImpl implements BookService { @Autowired private BookMapper bookMapper; @Override public boolean borrowBook(Long bookId, Long userId) { Book book = bookMapper.selectById(bookId); if (book == null || book.getStatus() != 0) { throw new RuntimeException("图书不存在或不可借阅"); } // 检查用户借阅数量等业务规则... // 创建借阅记录 BorrowRecord record = new BorrowRecord(); record.setBookId(bookId); record.setUserId(userId); record.setBorrowTime(new Date()); // 计算应还时间,例如30天后 record.setDueTime(calculateDueDate(30)); record.setStatus(1); borrowRecordMapper.insert(record); // 更新图书状态 book.setStatus(1); bookMapper.updateById(book); return true; } // ... 其他方法 }重要原则:Service层是处理业务规则、事务控制的地方。例如上面的借书操作,包含了“检查图书状态”和“更新图书状态并插入记录”两个数据库操作,这应该放在一个数据库事务中,确保要么全部成功,要么全部失败。可以通过在方法上添加
@Transactional注解来实现。
3.4 控制层(Controller)与前端交互
Controller负责接收HTTP请求,调用Service,并返回视图(ModelAndView)或数据(JSON)。
@Controller @RequestMapping("/book") public class BookController { @Autowired private BookService bookService; // 展示图书列表页 @GetMapping("/list") public String listBooks(@RequestParam(required = false) String name, @RequestParam(required = false) String author, Model model) { List<Book> books = bookService.getBooksByCondition(name, author); model.addAttribute("books", books); model.addAttribute("name", name); model.addAttribute("author", author); return "book/list"; // 对应 templates/book/list.html } // 处理借书请求 (假设是表单POST提交) @PostMapping("/borrow") public String borrowBook(@RequestParam Long bookId, HttpSession session, RedirectAttributes attributes) { Long userId = (Long) session.getAttribute("loginUserId"); if (userId == null) { attributes.addFlashAttribute("msg", "请先登录"); return "redirect:/user/login"; } try { bookService.borrowBook(bookId, userId); attributes.addFlashAttribute("successMsg", "借书成功!"); } catch (RuntimeException e) { attributes.addFlashAttribute("errorMsg", "借书失败:" + e.getMessage()); } return "redirect:/book/list"; // 重定向回列表页,防止表单重复提交 } // 提供RESTful API接口 (供前后端分离项目调用) @RestController @RequestMapping("/api/book") public class BookApiController { @GetMapping("/{id}") public Result getBookById(@PathVariable Long id) { Book book = bookService.getById(id); return Result.success(book); } } }关键点解析:
@GetMapping和@PostMapping:清晰地定义请求方法,符合RESTful风格。Model对象:用于向前端页面传递数据。HttpSession:用于获取登录用户信息,实现会话管理。RedirectAttributes:在重定向时传递一次性消息(如成功或错误提示),使用addFlashAttribute。- 防止重复提交:对于修改数据的操作(如POST),处理完成后应使用
redirect:进行重定向,而不是直接返回视图。这是Web开发中的一个重要实践。@RestController:如果做前后端分离,控制器可以返回JSON数据。自定义一个统一的Result类来包装返回结果(包含code, msg, data字段)是很好的实践。
3.5 前端页面(Thymeleaf模板)集成
在resources/templates/下创建HTML文件,使用Thymeleaf语法。book/list.html示例片段:
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>图书列表</title> <!-- 引入Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h2>图书列表</h2> <!-- 搜索表单 --> <form th:action="@{/book/list}" method="get" class="row g-3 mb-4"> <div class="col-auto"><input type="text" name="name" th:value="${name}" placeholder="书名" class="form-control"></div> <div class="col-auto"><input type="text" name="author" th:value="${author}" placeholder="作者" class="form-control"></div> <div class="col-auto"><button type="submit" class="btn btn-primary">搜索</button></div> </form> <!-- 消息提示 --> <div th:if="${successMsg}" class="alert alert-success" th:text="${successMsg}"></div> <div th:if="${errorMsg}" class="alert alert-danger" th:text="${errorMsg}"></div> <!-- 图书表格 --> <table class="table table-striped table-hover"> <thead><tr><th>ID</th><th>ISBN</th><th>书名</th><th>作者</th><th>状态</th><th>操作</th></tr></thead> <tbody> <tr th:each="book : ${books}"> <td th:text="${book.id}"></td> <td th:text="${book.isbn}"></td> <td th:text="${book.name}"></td> <td th:text="${book.author}"></td> <td> <span th:if="${book.status == 0}" class="badge bg-success">可借阅</span> <span th:if="${book.status == 1}" class="badge bg-warning">已借出</span> </td> <td> <a th:if="${book.status == 0}" th:href="@{/book/borrow(id=${book.id})}" class="btn btn-sm btn-outline-primary" onclick="return confirm('确认借阅吗?')">借阅</a> <!-- 管理员操作按钮 --> <a th:if="${session.user.role == 'admin'}" th:href="@{/book/edit/{id}(id=${book.id})}" class="btn btn-sm btn-outline-secondary">编辑</a> </td> </tr> </tbody> </table> </div> </body> </html>前端要点:
- Thymeleaf属性:
th:text用于动态替换文本,th:each用于循环,th:if用于条件判断,th:href用于动态生成链接。@{...}是Thymeleaf的URL表达式,能自动处理上下文路径。- 引入Bootstrap:强烈建议使用Bootstrap这类CSS框架,它能用极少的代码让你做出看起来专业、响应式的界面,把精力集中在功能逻辑上。
- 会话判断:通过
${session.user.role}可以判断用户角色,从而控制按钮的显示(如只有管理员能看到编辑按钮)。- 用户体验:关键操作(如删除、借阅)前使用JavaScript的
confirm()进行二次确认,是好习惯。
4. 项目调试、测试与部署上线
代码写完了,怎么确保它真的能跑起来,并且行为符合预期?
4.1 后端调试与单元测试
不要只靠“运行起来点一点”。编写简单的单元测试能极大提升代码质量。 使用Spring Boot Test和JUnit:
@SpringBootTest // 启动完整的Spring应用上下文进行集成测试 @AutoConfigureMockMvc // 配置MockMvc,用于模拟HTTP请求 class BookControllerTest { @Autowired private MockMvc mockMvc; @Test @WithMockUser(username = "admin", roles = {"ADMIN"}) // 模拟一个登录用户 void testGetBookList() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/book/list") // 模拟GET请求 .param("name", "Java") // 添加请求参数 .accept(MediaType.TEXT_HTML)) .andExpect(MockMvcResultMatchers.status().isOk()) // 断言状态码为200 .andExpect(MockMvcResultMatchers.view().name("book/list")) // 断言返回的视图名 .andExpect(MockMvcResultMatchers.model().attributeExists("books")); // 断言模型中有books属性 } }对于Service层,可以更纯粹地测试业务逻辑,使用@MockBean来模拟(Mock)依赖的Mapper。
调试技巧:善用IDE的调试功能(Debug),设置断点,观察变量值的变化流程。对于Web请求,可以使用Postman或Apifox等工具单独测试你的Controller API,这比在浏览器里测试更高效、更全面。
4.2 前端调试与浏览器工具
前端问题主要靠浏览器开发者工具(F12)。
- Console(控制台):查看JavaScript错误和日志输出。
- Network(网络):查看每一个HTTP请求的详情(URL、方法、状态码、请求头、请求体、响应体)。这是排查前后端数据交互问题的核心工具。如果点击按钮没反应,首先看Network里有没有发出请求,请求参数对不对,后端返回了什么。
- Elements(元素):查看和实时修改DOM结构、CSS样式,用于调试页面布局。
- Application(应用):查看和操作Cookie、Session Storage、Local Storage。
4.3 项目打包与部署
开发完成后,需要将项目打包成可独立运行的JAR包或WAR包。Spring Boot项目(打包为JAR):
- 在项目根目录下执行Maven命令:
mvn clean package。这会在target目录下生成一个your-project-0.0.1-SNAPSHOT.jar文件。 - 这个JAR包是“可执行”的,因为它内嵌了Tomcat服务器。只需在命令行运行:
java -jar your-project-0.0.1-SNAPSHOT.jar,应用就会启动。 - 你可以通过
application.yml配置服务器端口、数据库连接等。对于生产部署,通常会将JAR包上传到云服务器(如阿里云ECS),使用nohup java -jar ... &命令在后台运行,或者更专业地,使用systemd配置成系统服务。
传统Servlet/JSP项目(打包为WAR):
- 同样使用
mvn clean package,生成WAR文件。 - 将WAR文件复制到Tomcat安装目录的
webapps文件夹下。 - 启动Tomcat(
bin/startup.sh或startup.bat),Tomcat会自动解压并部署该应用。
部署注意事项:
- 数据库:确保部署环境的数据库IP、端口、用户名、密码与配置文件一致,并且已创建好对应的数据库和表结构。通常需要准备一个数据库初始化脚本(SQL文件)。
- 文件路径:如果项目中有文件上传功能,要确保在服务器上存在对应的存储目录,并且应用有读写权限。
- 端口冲突:检查服务器上8080(或其他你配置的端口)是否已被占用。
5. 项目文档撰写:从“有”到“优”
一份好的文档,是项目“完成度”的最终体现,也是你向老师展示思考过程和技术能力的最佳窗口。它不应该只是代码的重复描述。
5.1 文档的核心构成
一个完整的项目文档通常包括以下部分:
- 项目概述:用一两句话说明项目是做什么的,解决了什么问题。
- 技术栈:清晰列出使用的所有技术、框架、工具及其版本号(如Spring Boot 2.7.18, MySQL 8.0.33)。版本号非常重要!
- 系统架构设计:画出系统的架构图(如MVC三层架构图)、功能模块图、核心的E-R图(实体关系图,描述数据库表结构及关联)。一张清晰的图胜过千言万语。
- 数据库设计:提供详细的数据库表结构说明。最好用表格列出每个表的字段名、类型、是否为空、主键/外键、注释。
- 核心功能实现说明:挑选2-3个最具代表性或复杂的功能模块(如“借书流程”、“权限验证”),用“文字+流程图+关键代码片段”的形式,阐述你的实现思路和关键点。
- 部署运行指南:
- 环境要求(JDK, MySQL等版本)。
- 如何导入项目到IDE(如果是Maven项目,强调需要
mvn install下载依赖)。 - 如何初始化数据库(附上SQL脚本)。
- 如何配置
application.yml(关键配置项说明)。 - 如何启动项目(运行主类或打包后运行)。
- 访问地址(如
http://localhost:8080)和默认测试账号。
- 遇到的问题与解决方案:这是加分项!记录下你在开发过程中遇到的1-3个最棘手的Bug或技术难点,以及你是如何分析、排查并最终解决的。这体现了你的解决问题能力和学习过程。
5.2 让文档脱颖而出的技巧
- 使用专业工具绘图:用Draw.io、ProcessOn或Visio画架构图、流程图,不要用Word自带的简陋图形。
- 代码片段要精炼:只贴最核心、最能体现你设计思路的代码,并加上简要注释。避免大段粘贴整个类文件。
- 格式规范:使用Markdown编写文档(如README.md),结构清晰,便于在GitHub等平台直接展示。如果提交Word或PDF,也要注意排版美观。
- 真实可复现:确保文档中的每一步操作都是经过验证的,老师或同学能严格按照你的文档成功运行起项目。
6. 常见问题排查与避坑指南实录
结合我自己的经验和带新人时遇到的普遍问题,这里列一个“高频踩坑清单”:
6.1 数据库连接失败
- 现象:启动时报
Communications link failure或Access denied for user。 - 排查:
- 检查
application.yml中的url、username、password是否正确。 - 检查MySQL服务是否启动。
- 检查数据库用户是否有从本地(
localhost)或指定IP远程连接的权限。对于本地开发,确保用户名和密码正确。 - 特别注意URL中的时区参数:
serverTimezone=Asia/Shanghai。缺少这个,在某些MySQL版本下会导致连接成功但插入时间数据时出错。
- 检查
6.2 页面404 Not Found
- 现象:能访问首页,但点击某个链接或提交表单时出现404。
- 排查:
- 首先看浏览器地址栏的URL,与Controller中
@RequestMapping定义的路径是否完全一致(包括大小写)。 - 检查Controller方法上是否有
@Controller或@RestController注解。 - 检查请求方法(GET/POST)是否匹配(
@GetMappingvs@PostMapping)。 - 如果是返回视图的Controller,检查
return "pageName"对应的HTML文件是否在templates目录的正确子目录下。
- 首先看浏览器地址栏的URL,与Controller中
6.3 前端提交数据,后端接收为null
- 现象:表单填写了数据,但Controller方法中用
@RequestParam或实体对象接收时,字段全是null。 - 排查:
- 检查表单
<input>标签的name属性值,是否与后端接收参数的变量名一致。 - 如果是POST表单,检查
<form>标签是否有method="post"和enctype属性(普通表单通常是application/x-www-form-urlencoded,文件上传需要multipart/form-data)。 - 使用浏览器开发者工具的Network面板,查看请求的
Form Data部分,确认数据确实发送出去了。
- 检查表单
6.4 静态资源(CSS/JS/图片)加载失败
- 现象:页面能打开,但没有样式,或者图片不显示。
- 排查:
- Spring Boot默认将
static目录下的内容映射为静态资源。请确保你的CSS/JS文件放在src/main/resources/static/目录下。 - 在HTML中引用时,路径不要包含
static。例如,static/css/style.css在HTML中应写为href="/css/style.css"或使用Thymeleaf的th:href="@{/css/style.css}"。 - 检查浏览器开发者工具Network面板,看对静态资源的请求是否返回404,根据错误路径调整引用方式。
- Spring Boot默认将
6.5 事务不回滚
- 现象:Service方法中抛出异常,但数据库数据已经被部分修改。
- 原因:默认情况下,Spring的事务管理只对
RuntimeException和Error进行回滚,对检查型异常(Exception)不回滚。 - 解决:在Service方法上使用
@Transactional(rollbackFor = Exception.class),指定对所有异常都回滚。同时,确保异常被抛出,而不是在方法内部被捕获并处理了。
完成一个“完成度100%”的Java Web期末大作业,就像完成一次小型的全栈开发实战。它考验的不仅仅是你对某个API或语法的记忆,更是你系统分析、设计、实现、测试和文档化的综合能力。从选择一个合适的题目开始,到搭建环境、设计数据库、分层编码、集成调试,最后撰写文档,每一步都踩稳了,你收获的将不仅仅是一个分数,而是面对下一个真实项目时宝贵的经验和信心。记住,多动手、多思考、多记录(写文档和注释),遇到问题善用搜索引擎和调试工具,你一定能交出一份让自己和老师都满意的作品。
本文还有配套的精品资源,点击获取