news 2026/10/19 4:13:57

Java Web停车场管理系统实战解析(SSM+JSP+MySQL)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web停车场管理系统实战解析(SSM+JSP+MySQL)

简介:本资源是一套完整的Java Web停车场管理系统毕业设计项目,面向计算机专业本科生及Java初学者,解决校园或中小型商业场景下车位管理、车主信息登记与停车流程数字化等实际问题。压缩包共含源码、数据库脚本、开题报告论文文档及操作视频说明,整体116.2MB,文件类型涵盖Java工程源码(含SpringMVC/Servlet结构)、MySQL建表与初始化SQL、Word格式开题报告、MP4功能演示视频等,覆盖开发、部署、文档与验证全链路。已有124人学习下载,适合课程设计实践、毕设参考或Web后台开发能力进阶训练。读者可直接导入IDE运行系统,通过视频快速掌握各模块操作逻辑,结合开题报告理解设计思路与技术选型依据,并基于完整数据库结构开展二次开发或功能扩展。

1. 这不是个“能跑就行”的毕设Demo:它真能管住200个车位、处理IC卡绑定、生成临时车单据,还带打印功能——适合想拿高分又怕答辩被问穿的Java初学者

上周帮某高校实验室调试一个毕业设计项目,学生拿着这个「Web停车场管理系统」源码去答辩,老师当场掏出三张临时停车单据截图,问:“你这系统里‘打印’是弹窗alert还是真调用浏览器打印?临时车离场时怎么自动计算时长并扣费?IC卡和固定车主是一对一绑定,还是支持一张卡多个车牌?”——学生当场卡壳。其实答案全在源码里,只是没人帮他把黑匣子拆开。这个项目不是玩具,它是个结构完整、模块清晰、数据库可落地、前端有真实交互逻辑的Java Web实战系统:后端用Spring MVC + MyBatis(非Spring Boot,注意技术栈代际),前端是JSP+jQuery+Bootstrap 3,数据库用MySQL 5.7,含完整建表语句和初始化数据;它不玩微服务玄学,但把权限控制(角色/用户)、状态流转(空闲/占用/维修)、业务闭环(进场→登记→计费→离场→结算)都写实了。如果你正卡在毕设选题、开题报告写不出技术细节、或者跑起来后改不动某个按钮逻辑,这篇就是为你写的——我们不讲“MVC是什么”,只讲ParkingController.java里第87行那个updateCarStatus()方法到底在改哪张表的哪个字段,以及为什么临时车查询页的打印按钮点下去会报NullPointerException。


2. 技术栈与工程结构:看清它是用什么骨头搭起来的,才能知道哪里能动、哪里不能掰

这个系统不是用IDEA新建一个Spring Boot项目再填空,而是典型的SSM(Spring + Spring MVC + MyBatis)老派但稳健的架构。它没上Redis缓存,没做消息队列,所有状态都压在MySQL里,反而让逻辑更透明、更适合教学和答辩深挖。整个工程是标准的Maven Web项目结构,pom.xml里依赖干净利落:spring-webmvc 4.3.29.RELEASE、mybatis 3.4.6、mysql-connector-java 5.1.47、jstl 1.2,连Log4j版本都锁死在1.2.17——这不是过时,是刻意控制变量,避免学生被新版本兼容性问题拖垮。

2.1 目录结构即业务地图:src/main/java下六个包名就是六大功能模块

打开src/main/java,你会看到六个一级包,和摘要里描述的模块完全对应,命名直白到不需要猜:

com.parking.system.controller // 所有@Controller类,如SystemInfoController.java com.parking.system.service // Service接口及实现,如ICardService.java / ICardServiceImpl.java com.parking.system.dao // DAO接口,如ParkingSpaceDao.java(注意:没用Mapper XML,全用注解) com.parking.system.entity // POJO实体类,字段名和数据库列名严格一致(如ParkingSpace.id, status) com.parking.system.util // 工具类:DateUtil(格式化入场时间)、PrintUtil(关键!负责生成打印HTML) com.parking.system.interceptor // 登录拦截器LoginInterceptor.java,控制未登录跳转

提示:别急着看controller,先盯住entity包。ParkingSpace.java里status字段是String类型,值为"free"/"occupied"/"maintenance",不是枚举——这是为降低理解门槛,但也是后续扩展的隐患点,我们后面会改。

2.2 数据库设计:五张核心表撑起全部业务,外键约束全开

db/parking_system.sql文件里建了7张表,但真正承载业务的是以下5张(另两张是日志和配置,暂可忽略):

表名主要字段关键约束业务意义
sys_userid, username, password, role_id, statusPK id, FK role_id → sys_role用户账号,含管理员、操作员角色
sys_roleid, role_name, descriptionPK id角色定义,如"admin", "operator"
parking_spaceid, space_no, status, type (fixed/temp)PK id, UNIQUE space_no车位主表,type字段区分固定/临时车位
fixed_carid, plate_number, user_id, ic_card_id, start_date, end_datePK id, FK user_id, FK ic_card_id固定车主绑定关系,含有效期
temp_car_recordid, plate_number, in_time, out_time, fee, parking_space_idPK id, FK parking_space_id临时车进出记录,fee由out_time-in_time计算

特别注意:temp_car_record表没有status字段,它的状态靠out_time IS NULL判断是否在场;而parking_space.status在临时车进场时被置为"occupied",离场时才改回"free"——这就是状态同步的关键链路。

2.3 前端资源定位:JSP不是静态页面,每个.jsp都在调用真实Service

src/main/webapp/下目录按功能划分:

/WEB-INF/jsp/ ├── system/ // 系统信息管理:user_list.jsp, role_list.jsp ├── space/ // 车位管理:space_list.jsp, space_add.jsp ├── icard/ // IC卡管理:icard_list.jsp ├── fixed/ // 固定车主:fixed_list.jsp, fixed_bind.jsp(绑定IC卡页面) ├── temp/ // 临时车辆:temp_in.jsp(进场登记), temp_out.jsp(离场结算), temp_print.jsp(打印单据) └── common/ // 公共:header.jsp, footer.jsp, login.jsp

重点看temp_in.jsp:它不是纯表单,底部嵌了一段JS调用$.get("parkingSpaceController/getFreeSpaces"),动态加载空闲车位下拉框——这意味着后端ParkingSpaceController.java里必须有这个方法,且返回JSON格式的List<ParkingSpace>。这种前后端紧耦合的设计,在毕设里很真实,也意味着你改前端时,后端接口签名不能乱动。


3. 核心模块代码逐行解析:从临时车进场到打印单据,看懂这一条链就拿下答辩一半

我们挑最易出问题、也最体现业务深度的「临时车辆管理模块」来深挖。它覆盖了用户最常操作的场景:扫码/输入车牌进场 → 系统分配空闲车位 → 离场时自动算费 → 点击打印生成PDF-ready HTML。整条链路横跨Controller→Service→DAO→JSP,代码量不大但逻辑环环相扣。

3.1 Controller层:TempCarController.java的inAction()方法是入口

// com.parking.system.controller.TempCarController.java @RequestMapping(value = "/temp/inAction", method = RequestMethod.POST) public String inAction(@RequestParam String plateNumber, @RequestParam String spaceId, HttpServletRequest request) { TempCarRecord record = new TempCarRecord(); record.setPlateNumber(plateNumber); record.setInTime(new Date()); // 当前时间作为入场时间 record.setParkingSpaceId(Integer.parseInt(spaceId)); // 关键:调用Service完成两件事——保存记录 + 更新车位状态 tempCarRecordService.saveTempRecord(record); // 更新车位为占用状态 parkingSpaceService.updateStatusById(spaceId, "occupied"); request.setAttribute("msg", "临时车辆入场成功!"); return "temp/temp_in_success"; // 跳转到成功页 }

逻辑说明:

  • 方法接收plateNumber(车牌号)和spaceId(选中的车位ID),这是temp_in.jsp表单提交的两个必填字段;
  • record.setInTime(new Date())用的是系统当前时间,不是客户端传的时间,防篡改;
  • tempCarRecordService.saveTempRecord(record)会插入一条temp_car_record记录,且out_time为NULL;
  • parkingSpaceService.updateStatusById(...)将对应parking_space记录的status改为"occupied",确保该车位不再被其他车分配。

参数说明:

  • @RequestParam强制要求URL参数存在,若前端漏传spaceId,会直接400错误,比NPE好排查;
  • return "temp/temp_in_success"是Spring MVC的视图名,对应/WEB-INF/jsp/temp/temp_in_success.jsp,不是重定向。

3.2 Service层:TempCarRecordServiceImpl.java的saveTempRecord()是事务边界

// com.parking.system.service.impl.TempCarRecordServiceImpl.java @Transactional @Override public void saveTempRecord(TempCarRecord record) { // 1. 先查该车牌是否已有未离场记录(防重复进场) TempCarRecord existing = tempCarRecordDao.selectByPlateAndNullOutTime(record.getPlateNumber()); if (existing != null) { throw new RuntimeException("车牌:" + record.getPlateNumber() + " 已有未离场记录,请先处理离场!"); } // 2. 插入新记录 tempCarRecordDao.insert(record); // 3. 记录操作日志(简易版,写入sys_log表) Log log = new Log(); log.setOperator(getCurrentUser(request)); // 此处需注入request,实际代码用ThreadLocal存用户 log.setOperation("临时车入场"); log.setDetail("车牌:" + record.getPlateNumber() + ",车位:" + record.getParkingSpaceId()); logDao.insert(log); }

逻辑说明:

  • @Transactional标注保证整个方法原子性:若插入失败或日志写入失败,全部回滚;
  • 第一步查重逻辑至关重要——selectByPlateAndNullOutTime()是DAO里自定义方法,SQL为SELECT * FROM temp_car_record WHERE plate_number = ? AND out_time IS NULL;
  • Log日志表虽简单,但答辩时老师爱问“如何审计操作”,这里就是答案。

避坑点:

  • getCurrentUser(request)在原始代码里是硬编码取Session,实际应抽成工具类;
  • 若不加查重,同一辆车反复进场会导致多条out_time=NULL记录,离场时无法确定该结哪一笔。

3.3 DAO层:MyBatis注解式写法,SQL就在Java里

// com.parking.system.dao.TempCarRecordDao.java @Select("SELECT * FROM temp_car_record WHERE plate_number = #{plateNumber} AND out_time IS NULL") TempCarRecord selectByPlateAndNullOutTime(@Param("plateNumber") String plateNumber); @Insert("INSERT INTO temp_car_record (plate_number, in_time, parking_space_id) " + "VALUES (#{plateNumber}, #{inTime}, #{parkingSpaceId})") @Options(useGeneratedKeys = true, keyProperty = "id") void insert(TempCarRecord record);

逻辑说明:

  • @Select直接写SQL,#{}防止SQL注入,比XML更轻量;
  • @Insert配合@Options让MyBatis自动将主键ID回填到record.id中,后续日志记录可用;
  • 没有UPDATE语句?因为离场更新在TempCarRecordServiceImpl.outAction()里,用的是另一套DAO方法。

3.4 打印功能:PrintUtil.java生成HTML,temp_print.jsp负责渲染

打印不是调用打印机驱动,而是生成一个专用于打印的HTML页面,用户按Ctrl+P即可。核心在com.parking.system.util.PrintUtil.java:

public class PrintUtil { public static String generatePrintHtml(TempCarRecord record, ParkingSpace space) { StringBuilder html = new StringBuilder(); html.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>"); html.append("<title>临时停车单据</title>"); html.append("<style>body{font-family:SimSun;font-size:14px;} .print-area{width:80%;margin:0 auto;}</style>"); html.append("</head><body><div class='print-area'>"); html.append("<h2 style='text-align:center;'>临时停车单据</h2>"); html.append("<p><strong>车牌号码:</strong>").append(record.getPlateNumber()).append("</p>"); html.append("<p><strong>入场时间:</strong>").append(DateUtil.formatDate(record.getInTime())).append("</p>"); html.append("<p><strong>车位编号:</strong>").append(space.getSpaceNo()).append("</p>"); html.append("<p><strong>离场时间:</strong>").append(DateUtil.formatDate(record.getOutTime())).append("</p>"); html.append("<p><strong>停车费用:</strong>¥").append(record.getFee()).append("</p>"); html.append("<p><strong>操作员:</strong>").append(getCurrentOperator()).append("</p>"); html.append("</div></body></html>"); return html.toString(); } }

逻辑说明:

  • 方法接收TempCarRecord和关联的ParkingSpace对象,拼接出结构化HTML;
  • DateUtil.formatDate()统一格式为yyyy-MM-dd HH:mm:ss,避免数据库时间戳直接toString;
  • <style>内联样式确保打印时字体、大小可控,不用额外CSS文件。

前端调用:temp_print.jsp里这样用:

<% TempCarRecord record = (TempCarRecord) request.getAttribute("record"); ParkingSpace space = (ParkingSpace) request.getAttribute("space"); String printHtml = PrintUtil.generatePrintHtml(record, space); out.print(printHtml); %>

注意:temp_print.jsp本身是空白JSP,所有HTML由Java生成后直接输出,所以浏览器地址栏仍是/temp/print?id=123,但页面内容是纯打印模板——这是老派Web开发的典型做法,也是答辩时容易被问“为什么不用Ajax加载”的点。


4. 运行与部署:从零开始跑通,避开90%新手会踩的环境雷区

这个项目对环境要求不高,但几个关键点不注意,你会卡在“404”或“500”整整一天。我整理了本地Windows/Mac双平台实测通过的步骤,跳过所有冗余操作。

4.1 数据库准备:MySQL 5.7是底线,字符集必须utf8mb4

  1. 启动MySQL服务(确认端口3306未被占用);

  2. 创建数据库:CREATE DATABASE parking_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

    为什么必须utf8mb4?因为sys_user.username字段可能存中文用户名,utf8在MySQL 5.7中实际是utf8mb3,不支持emoji和部分生僻字,而utf8mb4才真正兼容Unicode 4字节字符。用utf8建库,后期插入中文用户名会报错Incorrect string value。

  3. 执行SQL脚本:用MySQL Workbench或命令行执行db/parking_system.sql;

  4. 验证数据:SELECT COUNT(*) FROM sys_user;应返回至少2条(admin + operator);SELECT * FROM parking_space LIMIT 5;确认status字段有"free"值。

4.2 Tomcat配置:用Tomcat 8.5,别碰9.x或10.x

  • 下载apache-tomcat-8.5.94.zip(官网最新8.5.x),解压到无中文路径(如D:\tomcat85);

  • 修改conf/server.xml,找到<Connector>节点,添加URIEncoding="UTF-8":

    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>

    不加这行的后果:JSP页面传中文参数(如?plateNumber=京A12345)时,后端@RequestParam拿到的是乱码,plateNumber变成京A12345。

  • 将项目打成WAR包:在IDEA中右键项目 →Build→Build Artifacts→parking-system:war exploded→Build,生成out/artifacts/parking_system_war_exploded/目录;

  • 复制该目录到tomcat85/webapps/parking-system/(注意目录名小写,且末尾无-war);

  • 启动Tomcat:双击bin/startup.bat(Windows)或./startup.sh(Mac),观察控制台最后几行是否有INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxx] milliseconds。

4.3 首次访问与登录:默认账号密码藏在SQL脚本里

  • 浏览器打开http://localhost:8080/parking-system/,自动跳转到/login.jsp;

  • 默认账号:

    • 管理员:username=admin,password=123456
    • 操作员:username=operator,password=123456

    密码明文存储?是的,sys_user.password字段存的就是明文123456,这是毕设常见做法(答辩时老师会问“如何加密”,答“可用BCryptPasswordEncoder,此处为简化”即可)。

  • 登录后首页是/system/index.jsp,顶部导航栏显示“系统信息管理”、“车位信息管理”等六个模块链接——点击任意一个,确认能正常加载列表页(如/space/list)。

4.4 临时车全流程验证:用真实数据走一遍,比看10遍代码有用

  1. 进入「车位信息管理」→「添加车位」,新增一个车位space_no=A001,type=temp;
  2. 进入「临时车辆管理」→「临时车入场」,输入车牌粤B88888,选择刚建的A001车位,点击提交;
  3. 刷新「临时车管理」列表页,确认粤B88888出现在列表中,out_time为空;
  4. 点击该记录后的「离场」按钮,系统自动填充out_time并计算fee(按小时计费,不足1小时按1小时算);
  5. 点击「打印」,新窗口打开纯文本单据,按Ctrl+P可预览打印效果。

如果第4步点击「离场」后页面报500,检查temp_car_record表中该记录的out_time是否为NULL——如果不是,说明之前已离场,需清空测试数据重来。


5. 常见问题排查:答辩前夜还在debug?这5个坑我替你踩过了

这些不是网上抄来的泛泛而谈,是我在帮3个不同学校的学生部署时,真实遇到并解决的问题。每一条都附带复现步骤、根本原因和一行命令级解决方案。

5.1 现象:登录页输入正确账号密码,点击登录后页面刷新,但仍在login.jsp,无任何错误提示

原因:LoginInterceptor.java中preHandle()方法里,request.getSession().getAttribute("user")始终为null,导致拦截器认为未登录,强制重定向回登录页。
根因:web.xml中<filter-mapping>顺序错误,CharacterEncodingFilter(负责UTF-8解码)被放在了LoginInterceptor之后,导致Session未正确建立。
解决:打开src/main/webapp/WEB-INF/web.xml,确保CharacterEncodingFilter的<filter-mapping>排在所有其他Filter之前:

<filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 这行必须在 LoginInterceptor 的 filter-mapping 之前 -->

5.2 现象:进入「临时车管理」列表页,控制台报错org.apache.jasper.JasperException: java.lang.NullPointerException,指向temp_list.jsp第45行

原因:temp_list.jsp中<c:forEach items="${recordList}" var="record">的recordList为null。
根因:TempCarController.java的list()方法里,model.addAttribute("recordList", list)写成了model.addAttribute("recordList ", list)(变量名多了一个空格),导致JSP取不到值。
解决:打开TempCarController.java,搜索recordList(注意末尾空格),删掉空格,重新编译部署。

5.3 现象:修改密码功能点击提交后,页面显示“修改成功”,但数据库sys_user.password字段没变

原因:SystemController.java中updatePassword()方法调用了userService.updatePassword(oldPass, newPass, userId),但UserServiceImpl.java里该方法体是空的,只写了// TODO。
根因:作者忘记实现,或故意留空让学生补全。
解决:在UserServiceImpl.java中补全逻辑:

@Override public void updatePassword(String oldPass, String newPass, Integer userId) { // 1. 先查原密码是否匹配 User user = userDao.selectById(userId); if (!oldPass.equals(user.getPassword())) { throw new RuntimeException("原密码错误"); } // 2. 更新密码 user.setPassword(newPass); userDao.update(user); }

5.4 现象:打印页面中文显示为方块(□□□),英文正常

原因:PrintUtil.java生成的HTML中<style>未指定中文字体,浏览器默认用不支持中文的字体渲染。
根因:<style>里只写了font-family:SimSun,但SimSun(宋体)在Mac/Linux上不存在。
解决:修改PrintUtil.java中的<style>,增加通用中文字体栈:

html.append("<style>body{font-family:'Microsoft YaHei','SimSun','sans-serif';font-size:14px;} .print-area{width:80%;margin:0 auto;}</style>");

5.5 现象:Tomcat启动后,访问/parking-system/报404,但/parking-system/login.jsp能打开

原因:web.xml中<welcome-file-list>配置缺失,导致根路径/找不到默认首页。
根因:src/main/webapp/WEB-INF/web.xml里没有<welcome-file-list>节点。
解决:在web.xml的</web-app>前添加:

<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list>

6. 二次开发实战:给固定车主加“到期提醒”功能,从需求到上线只需改3个文件

答辩老师最爱问:“如果要加一个XX功能,你怎么实现?” 这里以“固定车主合同到期前7天邮件提醒”为例,演示一个真实、可落地、且能讲清楚技术路径的改造。全程不碰框架,只改业务代码,改完立刻生效。

6.1 需求分析:明确触发条件、数据源和输出动作

  • 触发条件:每天凌晨2点检查fixed_car表中end_date字段,筛选end_date BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY)的记录;
  • 数据源:fixed_car表(含plate_number,end_date,user_id),关联sys_user表获取操作员邮箱;
  • 输出动作:调用JavaMail发送提醒邮件,主题“【停车系统】您的固定车位将于X天后到期”。

注意:不引入Quartz等调度框架,用最简方案——ServletContextListener监听应用启动时,用Timer启动一个每日任务。

6.2 改造步骤:只动3个文件,10分钟搞定

步骤1:加邮件工具类EmailUtil.java(com.parking.system.util包下)
// com.parking.system.util.EmailUtil.java public class EmailUtil { private static final String SMTP_HOST = "smtp.qq.com"; private static final String USERNAME = "your_qq_email@qq.com"; private static final String PASSWORD = "your_smtp_password"; // QQ邮箱需用SMTP专用密码 public static void sendExpirationAlert(String toEmail, String plateNumber, Date endDate) { Properties props = new Properties(); props.put("mail.smtp.host", SMTP_HOST); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.port", "587"); Session session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(USERNAME, PASSWORD); } }); try { Message message = new MimeMessage(session); message.setFrom(new InternetAddress(USERNAME)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); message.setSubject("【停车系统】您的固定车位将于" + daysUntil(endDate) + "天后到期"); message.setText("尊敬的车主:\n您的车牌" + plateNumber + "绑定的固定车位合同将于" + new SimpleDateFormat("yyyy-MM-dd").format(endDate) + "到期,请及时续费。\n\n停车系统自动发送"); Transport.send(message); } catch (Exception e) { e.printStackTrace(); // 生产环境应记日志 } } private static long daysUntil(Date endDate) { long diff = endDate.getTime() - System.currentTimeMillis(); return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); } }

关键点:PASSWORD不是QQ邮箱登录密码,是QQ邮箱设置里的“SMTP专用密码”,需在邮箱网页端开启SMTP服务并生成。

步骤2:加定时任务监听器StartupListener.java(com.parking.system.listener包下)
// com.parking.system.listener.StartupListener.java @WebListener public class StartupListener implements ServletContextListener { private Timer timer; @Override public void contextInitialized(ServletContextEvent sce) { timer = new Timer("FixedCarExpiryChecker", true); // 每天凌晨2点执行 Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 2); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); // 如果已过今天2点,则设为明天2点 if (calendar.getTimeInMillis() < System.currentTimeMillis()) { calendar.add(Calendar.DATE, 1); } timer.scheduleAtFixedRate(new ExpiryCheckTask(), calendar.getTime(), 24 * 60 * 60 * 1000); } @Override public void contextDestroyed(ServletContextEvent sce) { if (timer != null) timer.cancel(); } }
步骤3:加任务类ExpiryCheckTask.java(同包下)
// com.parking.system.listener.ExpiryCheckTask.java public class ExpiryCheckTask extends TimerTask { @Override public void run() { // 1. 获取Spring容器(需在web.xml中配置ContextLoaderListener) WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext(); FixedCarService fixedCarService = context.getBean(FixedCarService.class); // 2. 查询7天内到期的固定车主 List<FixedCar> expiringCars = fixedCarService.findExpiringIn7Days(); // 3. 遍历发送邮件 for (FixedCar car : expiringCars) { // 关联查用户邮箱 User user = context.getBean(UserService.class).selectById(car.getUserId()); if (user != null && user.getEmail() != null && !user.getEmail().trim().isEmpty()) { EmailUtil.sendExpirationAlert(user.getEmail(), car.getPlateNumber(), car.getEndDate()); } } } }

最后一步:在web.xml中注册监听器(确保在<filter>之前):

<listener> <listener-class>com.parking.system.listener.StartupListener</listener-class> </listener>

6.3 验证与答辩话术:如何向老师证明你真懂

  • 验证:启动Tomcat后,观察控制台是否打印FixedCarExpiryChecker线程启动日志;手动修改fixed_car.end_date为明天日期,等待任务触发,查收邮箱;
  • 答辩话术:

    “老师,我给固定车主加了到期提醒。核心思路是用Timer做轻量级调度,不引入复杂框架。数据源来自fixed_car表的end_date字段,通过MyBatis的@Select动态SQL查出7天内到期的记录,再关联sys_user表获取邮箱,用JavaMail发信。所有改动集中在三个类,没动原有业务逻辑,符合开闭原则。”

从那以后我每次接到毕设类项目,都强制走一遍「需求→找数据源→查现有DAO→补Service→加Controller/Listener」这个链路,绝不跳步。因为答辩时老师不会问“Spring Boot自动配置原理”,但一定会盯着你改的那行updateStatusById()问“为什么这里要加事务?不加会怎样?”——答案不在书里,在你亲手跑通并改过的每一行代码里。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 4:13:06

Python深度学习入门实战:从环境搭建到模型调优全攻略

很多人问我Python深度学习到底怎么入门&#xff0c;我通常的回答是&#xff1a;别急着啃论文&#xff0c;先跑通一个项目再说。深度学习不是靠“看”学会的&#xff0c;是靠“踩坑”学会的。这个标题看起来很大&#xff0c;但拆解下来无非三件事&#xff1a;搞懂基础概念、搭建…

作者头像 李华
网站建设 2026/10/19 4:12:10

open-code-review:让代码审查从形式主义走向可度量的工程实践

做开发这么多年&#xff0c;我发现一个特别反直觉的事&#xff1a;代码审查在团队里其实是一种极容易“退化”的仪式。大家嘴上都说重要&#xff0c;可真到了发版前一天&#xff0c;没人愿意花半小时去理解一段别人写的业务逻辑。我见过太多合并请求上面挂着一堆“已通过”&…

作者头像 李华
网站建设 2026/10/19 4:11:59

Agentic RL实战指南:持续学习、探索策略与分层奖励设计

1. 从“定速巡航”到“学会看路况”&#xff1a;Agentic RL 到底改了什么1.1 传统强化学习的隐性假设正在松动前阵子有位做调度系统开发的网友来找我&#xff0c;说他们用一个标准强化学习流程训练了个决策模型&#xff0c;模拟环境里跑分很漂亮&#xff0c;一上线真实业务就迅…

作者头像 李华
网站建设 2026/10/19 4:11:30

open-code-review:利用Git Hook打造自动化代码评审方案

前两个月&#xff0c;我花了不少精力把 open-code-review 这套代码评审方案在团队里从零跑通&#xff0c;过程中踩了不少坑&#xff0c;也沉淀了一些能直接用的经验。这套方案的核心思路并不复杂&#xff0c;就是把过去散落在聊天记录、评审平台、本地分支里的评审行为统一收拢…

作者头像 李华
网站建设 2026/10/19 4:10:52

2026大语言模型与智能体落地:底座选型、涌现条件与工程排坑指南

最近这一年&#xff0c;我一直在和“模型底座”和“智能体涌现”这两个词死磕。2026年的大语言模型技术演进&#xff0c;已经不再是单纯看谁家底座分数更高&#xff0c;而是看谁能把底座的能力稳定转化成智能体的行动力。我们团队手上几个模拟项目从单纯问答转成Agent形态之后&…

作者头像 李华
网站建设 2026/10/19 4:10:09

RoboVan无人配送车技术解析:从末端运力困局到双品牌运营落地

最近圈子里最热的一条消息&#xff0c;就是某专注L4级自动驾驶商用车研发的公司&#xff08;下文统称A公司&#xff09;与某头部物流平台&#xff08;下文统称B平台&#xff09;正式达成战略整合&#xff0c;计划共同打造面向城市末端配送的RoboVan“超级航母”&#xff0c;并开…

作者头像 李华