简介:这是一份基于SSM框架的智慧养老院系统毕业设计源码与数据库打包资料,适用于计算机相关专业学生完成毕业设计、期末大作业或课程设计,也适合想要上手Java Web项目的小白参考。系统覆盖养老院日常管理场景,包括老人档案信息、床位分配、护工排班、消息通知、员工管理等核心模块,采用JSP作为前端页面,后端以Spring、SpringMVC、MyBatis分层构建,整体业务逻辑清晰。压缩包共343个文件,大小约34.39MB,包含Java源码、JSP页面、class编译文件、jar依赖包、SQL脚本,以及JS、CSS、PNG、GIF等前端资源,其中GIF可用于查看操作演示,SQL脚本可直接初始化数据库,导入Eclipse或IDEA即可运行。目前已有310人学习下载。项目经过老师指导并获高分,代码结构规范,Controller、Service、Mapper分层明确,内置完整业务闭环,可直接作为毕设展示或二次开发基础,能够帮助新手快速理解SSM整合流程、数据表设计思路与前后端交互过程,节省从零搭建的时间。
1. 基于SSM的智慧养老院系统:这份毕业设计源码包到底在研究什么
打开“基于SSM的智慧养老院系统的设计与实现源码+数据库(毕业设计).zip”,里面是一套典型的Java Web毕业设计工程:Spring、SpringMVC、MyBatis三个框架串成后端,MySQL存数据,Tomcat跑服务,前端以JSP页面为主。所谓“智慧养老院”,落到代码层面就是人、房、护、医四类数据的管理系统——老人档案、床位分配、护工排班、健康记录、用药信息、来访登记、费用账单,全部通过浏览器页面完成增删改查。
这类项目在本科毕业设计里出现频率极高,原因很实际:SSM是很多高校Java方向课程教过的技术栈,业务模型清晰,功能边界好划分,数据库设计也能体现学生的建模能力。拿到这套源码的人,多半不是要直接上线商用,而是要完成三件事:把环境跑通、看懂代码路径、在原有基础上改出一两个自己的亮点去参加答辩。
这篇笔记不打算逐行读代码,而是按“系统是什么→SSM怎么协作→数据库怎么设计→怎么跑起来→坑在哪→答辩扩展”的顺序,把拿到zip之后最可能遇到的技术问题说透。适合正在做毕业设计的学生,也适合接手别人Java Web项目想快速上手的开发者。
2. SSM三层架构协作拆解:Spring、SpringMVC、MyBatis在养老院系统里各自管什么
SSM不是三块独立代码堆在一起,而是一条链路:请求先由SpringMVC接收,业务逻辑由Spring容器里的Service对象执行,数据访问最终落到MyBatis生成的SQL上。读完这条链路,后面改功能、修bug才有方向感。这一章用养老院系统最常见的“查询老人列表”场景,把三个框架的分工讲透。
2.1 Spring管什么:IoC容器、Service层事务与数据源托管
Spring在这个项目里不直接处理浏览器请求,它管理的是对象之间的依赖关系。所有Service对象、Mapper代理对象、数据源、事务管理器都由Spring容器创建和装配。以“添加一位新入住老人”为例,这一步要同时往elder表插入档案、把room表的床位状态改成已占用,两步必须在一个事务里完成,否则会出现“档案建了但床位没锁”的脏数据。事务边界由Spring控制,默认遇到运行时异常就回滚。
典型的Spring配置文件长这样:
<!-- applicationContext.xml 核心片段 --> <context:component-scan base-package="com.example.nursinghome.service"/> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.nursinghome.mapper"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>这里component-scan负责把Service包下的类注册成Bean;property-placeholder读取jdbc.properties里的数据库连接参数;sqlSessionFactory把DataSource交给MyBatis,同时指定Mapper XML的位置;MapperScannerConfigurer的作用是按basePackage自动生成Mapper接口的代理对象,这也是Service里能直接@Autowired注入Mapper接口的原因。事务管理器绑定数据源后,Service方法上的@Transactional注解才会生效。这套配置在SSM项目里几乎是模板级的存在,拿到源码后第一件事就是确认这几个bean都在,并且包名和你目录里的实际包一致。
2.2 SpringMVC管什么:请求分发、参数绑定与视图解析
SpringMVC是系统的入口层。浏览器发来的URL请求先被web.xml里配置的DispatcherServlet截获,再由它根据@RequestMapping注解找到对应Controller的方法。养老院系统的登录、老人列表、床位分配、费用查询,每一个功能点都对应Controller类里的一两个方法。
web.xml里的配置决定了DispatcherServlet的拦截范围:
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>注意url-pattern这里用的是“/”,不是“/*”。写成/*会把JSP页面也拦截掉,导致所有视图跳转都变成404,这是SSM项目最高频的配置错误之一。而加载spring-mvc.xml之后,框架才知道该扫描哪个包下的Controller、怎么解析视图、哪些路径放行静态资源:
<!-- spring-mvc.xml 核心片段 --> <context:component-scan base-package="com.example.nursinghome.controller"/> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:resources mapping="/static/**" location="/static/"/>InternalResourceViewResolver的prefix和suffix决定了Controller返回的字符串最终指向哪个JSP文件。比如方法return "elder/list",实际渲染的就是/WEB-INF/jsp/elder/list.jsp。mvc:resources则把/static/开头的CSS、JS、图片请求放行,不让DispatcherServlet拦截。很多项目页面打开了但样式全丢,就是少了这行静态资源映射。
2.3 MyBatis管什么:Mapper接口与SQL映射文件如何对得上
MyBatis负责最后的SQL执行。它比直接写JDBC舒服的地方在于:SQL写在XML文件里,参数和结果集可以自动映射。养老院系统里每张表基本对应一个Mapper接口和一个Mapper XML文件。接口定义方法,XML写SQL,两者通过“接口全限定名 + 方法名”绑定。
public interface ElderMapper { List<Elder> selectAll(); Elder selectById(Integer id); int insertElder(Elder elder); int updateStatus(@Param("id") Integer id, @Param("status") Integer status); }<mapper namespace="com.example.nursinghome.mapper.ElderMapper"> <select id="selectAll" resultType="com.example.nursinghome.pojo.Elder"> SELECT * FROM elder </select> <select id="selectById" parameterType="java.lang.Integer" resultType="com.example.nursinghome.pojo.Elder"> SELECT * FROM elder WHERE id = #{id} </select> <insert id="insertElder" parameterType="com.example.nursinghome.pojo.Elder" useGeneratedKeys="true" keyProperty="id"> INSERT INTO elder(name, gender, birth_date, room_id, entry_date, status) VALUES (#{name}, #{gender}, #{birthDate}, #{roomId}, #{entryDate}, #{status}) </insert> </mapper>namespace必须等于接口的全限定名,statement的id必须等于方法名,这是MyBatis绑定的两条硬规则。resultType直接写实体类的全限定名;如果字段名和属性名对不上,就得改用resultMap做显式映射。参数绑定用#{},MyBatis会把它编译成预编译语句的占位符,能防SQL注入;如果写成${}就是字符串拼接,只在少数需要动态传表名或排序字段的场景才用。参数说明:@Param注解的作用是给多个参数起名字,让XML里能用#{id}、#{status}引用,不加的话MyBatis只能用arg0、param1这种很难维护的方式。
2.4 一条完整请求链路:查老人列表从点击到页面渲染经过了什么
把三层串起来看一个真实场景。管理员在导航栏点击“老人管理”,页面向服务端发一个GET /elder/list请求。链路是这样的:
- Tomcat收到请求后交给DispatcherServlet;
- DispatcherServlet根据/elder/list找到ElderController里@RequestMapping("/elder")下@RequestMapping("/list")对应的方法;
- 方法调用elderService.listAll();
- Service方法上有@Transactional时,Spring先开启事务,再调用ElderMapper.selectAll();
- MyBatis通过SqlSession执行XML里的SELECT * FROM elder,把结果集映射成List 返回;
- 数据逐层回到Controller,被塞进Model;
- 视图解析器把返回字符串"elder/list"解析成JSP路径;
- JSP用EL表达式和JSTL标签遍历elderList,渲染成HTML表格返回浏览器。
每一步的数据形态都不同:Controller里是Java对象,Service里是业务逻辑和数据校验,Mapper里是SQL参数和结果映射。排查问题时先判断症状发生在哪一段——控制台有SQL日志但页面没数据,问题在JSP渲染;SQL没执行,问题在请求没进Controller或Service抛了异常。理解这条链路,后面动手改需求就敢下手了。
3. 数据库设计:智慧养老院的表结构、关联关系与SQL导入要点
数据库是答辩时老师最爱深挖的部分,因为SSM项目里几乎所有功能都能映射成一张表的增删改查,表设计直接决定系统能讲出多少业务故事。这一章按主数据、过程数据、关系设计三层拆解,最后给出SQL脚本导入的完整操作。
3.1 主数据表:老人档案、护工、房间床位怎么建
主数据表是系统的“底表”,存的是当前状态。养老院系统里至少有三类:老人档案表、护工表、房间床位表。老人档案表是最核心的,几乎所有业务表都通过elder_id和它关联。
CREATE TABLE elder ( id INT AUTO_INCREMENT COMMENT '老人ID', name VARCHAR(50) NOT NULL COMMENT '姓名', gender TINYINT COMMENT '性别:1男 2女', birth_date DATE COMMENT '出生日期', id_card VARCHAR(18) COMMENT '身份证号', phone VARCHAR(20) COMMENT '本人电话', room_id INT COMMENT '关联room表床位ID', entry_date DATE COMMENT '入住日期', status TINYINT DEFAULT 1 COMMENT '状态:1在住 0已退住', remark VARCHAR(255) COMMENT '备注', PRIMARY KEY (id), INDEX idx_room (room_id), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='老人档案表';字段设计有几个讲究。身份证号虽然是唯一的,但现实中存在没登记或登记不完整的情况,所以一般不建唯一索引,保留可空。room_id是外键语义的字段,指向床位表,表示老人住在哪个房间;一个房间可以住多个老人(养老院常见双人间),所以elder对room是多对一。status字段是逻辑删除的开关,值为0就表示退住,但数据行还留在表里,这在毕业设计里是加分项,后面3.3节细说。列表页查询条件基本是“按状态过滤 + 按姓名模糊查询 + 分页”,所以给status建普通索引就够了,不需要在remark上建索引。
房间床位表通常包含房号、床号、房间类型(单人间/双人间/护理间)、当前状态(空闲/已入住)、楼层这几列。护工表则相对简单:姓名、手机号、负责区域、班次。这里要注意,护工和老人是多对多关系——一个护工照顾多个老人,一个老人也会接受不同护工的轮班护理,所以一般不直接在外键字段里表达,而是靠护理记录表来承担关联,这就引出3.2节。
3.2 过程记录表:健康、用药、护理、来访、费用五张流水表
过程记录表存的是“发生过什么”,是典型流水账结构,只增不改(修改也极少)。养老院系统的业务厚度全靠这五张表撑起来:
| 表名 | 记录内容 | 核心字段 | 对应业务模块 |
|---|---|---|---|
| health_record | 每日健康监测 | elder_id, blood_pressure, blood_sugar, temperature, record_date | 健康档案 |
| medication_record | 用药执行记录 | elder_id, drug_name, dosage, exec_time, executor | 用药管理 |
| nursing_record | 护理操作记录 | elder_id, nurse_id, content, create_time | 护理管理 |
| visit_record | 家属来访登记 | elder_id, visitor_name, visitor_phone, relation, in_time, out_time | 出入管理 |
| fee_record | 费用流水 | elder_id, fee_type, amount, pay_status, create_time | 费用管理 |
这五张表有共同特征:都带elder_id作为关联字段,都带时间字段,都有一条“谁做的/谁支付的”操作人信息。健康记录表是系统演示时的重点,因为它能体现“智慧”——老人生命体征数据按天沉淀,页面可以按时间线展示,也可以做简单的异常值标红。费用表则要注意amount字段用DECIMAL而不是FLOAT,金额精度在MySQL里用浮点类型是经典翻车点,答辩时被问到“为什么不用double”能答上来就是加分。
设计过程表时的习惯:时间字段统一用datetime,不要混用timestamp和varchar存时间;操作人字段不要直接存姓名,存nurse_id或admin_id,需要展示时再联表查,避免数据冗余。这些细节都是答辩时能展示“做过思考”的地方。
3.3 外键、逻辑删除与索引:答辩时最常被问的三个数据库问题
第一个问题是“为什么没建外键”。很多教科书里强调外键约束,但实际项目里常见做法是不建物理外键,只保留逻辑外键字段(比如elder.room_id),原因有二:一是物理外键会让插入、更新变慢,维护成本高;二是删除数据时外键约束会挡住操作,演示流程容易卡住。答辩话术是“用应用层逻辑保证引用完整性”。
第二个问题是逻辑删除。物理删除数据行会让历史记录消失,健康记录、费用流水这些表一旦被删,系统就没有可讲的故事了。所以elder表保留status字段,删除操作实际是UPDATE status=0,查询列表默认带一条and status = 1的条件。这个设计也留了“后悔药”——演示时误删了数据,改回status就能恢复。
第三个问题是索引。索引不是越多越好,过程记录表上的索引建议聚焦在elder_id和时间字段上,因为查询模式基本上都是“查某个老人的历史记录”。联表查询在SSM里很常见,比如页面要展示老人的姓名、房间号、护工姓名,习惯写法是JOIN而不是三张表各查一遍:
SELECT e.id, e.name, e.phone, r.room_no, r.bed_no, n.name AS nurse_name FROM elder e LEFT JOIN room r ON e.room_id = r.id LEFT JOIN nursing_record nr ON nr.elder_id = e.id LEFT JOIN nurse n ON nr.nurse_id = n.id WHERE e.status = 1 GROUP BY e.id;LEFT JOIN保证没有护理记录的老人也能查出主信息;GROUP BY是因为一个老人可能有多条护理记录,需要按elder_id去重。这种SQL在答辩时被要求手写,能写出来就是实打实的加分项。
3.4 SQL脚本导入两条路:命令行与图形化工具,附字符集检查
拿到数据库脚本后,第一步是确认脚本是“建库+建表+数据”的全量脚本,还是只有建表语句。看文件开头的几行就能判断,通常会有CREATE DATABASE或者USE开头的语句。导入有两种方式,我一般优先用命令行,速度快且报错信息直白:
# 先建库(如果脚本里没有CREATE DATABASE) mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS nursing_home DEFAULT CHARSET utf8mb4;" # 导入SQL文件 mysql -u root -p nursing_home < nursing_home.sql如果脚本第一行是“CREATE DATABASE nursing_home”,那就不需要建库,直接执行mysql -u root -p < nursing_home.sql即可。导入后用一条SQL确认表数量和数据量:
SELECT COUNT(*) AS table_count FROM information_schema.tables WHERE table_schema = 'nursing_home';图形化工具(比如Navicat或MySQL Workbench)的操作更简单,右键数据库选“运行SQL文件”就行,但要注意:工具连接时选的字符集要和SQL文件一致,否则中文数据会变成乱码。检查SQL脚本开头有没有SET NAMES utf8mb4这条语句,没有就手动在文件头部加一行,再导入。这一步是后续所有中文显示问题的源头,值得多花一分钟。
4. 把zip里的源码跑起来:环境选型、项目导入、配置修改与首次启动
源码包到手以后,第一目标不是写代码,是让这套系统在本机跑起来。这一步95%的坑都出在“环境版本和配置文件没对齐”。这一章给一套稳妥的落地路径:先定版本,再导工程,然后改配置,最后启动验证。
4.1 版本搭配清单:JDK、Maven、Tomcat、MySQL选什么最稳
SSM毕业设计的代码通常基于某个Java课程的教学版本编写,兼容性最好的组合是:
| 组件 | 建议版本 | 说明 |
|---|---|---|
| JDK | 1.8 | SSM项目最通用的编译版本,勿用JDK 11+直接跑老代码 |
| Maven | 3.6.x | 与IDE内置Maven兼容性最好 |
| Tomcat | 8.5或9.0 | 支持Servlet 3.1规范,SSM项目的主流容器 |
| MySQL | 5.7或8.0 | 5.7最省事,8.0要注意驱动和时区参数 |
| IDE | 主流Java IDE | 社区版即可,重点是能识别Maven工程 |
版本搭配上有一条重要原则:不要追求新。JDK 8是很多SSM项目编译时的基准,用高版本JDK编译老工程容易遇到javax包被移除、反射告警变报错等问题。Tomcat 9和JDK 8兼容良好,但如果项目里用了某些老版本的Servlet API,Tomcat 8.5更保险。MySQL 8.0可以跑,但连接驱动不能再用老版本,url参数也要加时区,具体见4.3节。
4.2 用IDE导入Maven工程:解压、识别、依赖下载与pom检查
先把zip解压,确认目录结构里有没有pom.xml。SSM的Maven工程标准结构是:src/main/java放Java源码,src/main/resources放配置文件,src/main/webapp放JSP和静态资源,pom.xml在根目录。用IDE打开时选择pom.xml作为Maven项目导入,而不是新建空项目再把代码拖进去,否则依赖解析和目录识别都会出问题。
pom.xml的核心依赖结构通常是这样的(具体版本号以你解压出来的pom.xml为准):
<properties> <spring.version>5.1.8.RELEASE</spring.version> <mybatis.version>3.5.3</mybatis.version> </properties> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- MyBatis 核心 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!-- MyBatis 与 Spring 整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies>导入后第一步是等依赖下载完成,观察IDE右下角的进度。如果下载很慢或卡住,多半是默认Maven中央仓库连接不稳定,解决方法是把Maven的settings.xml里的镜像换成国内镜像地址,然后重新导入。依赖下载完成后,在项目根目录执行mvn clean install -DskipTests验证构建是否通过。构建报错时先看第一个错误,不要被后面的连锁报错带偏——不少时候只是某个依赖没下载完整,执行clean后再重新构建就好了。
4.3 修改数据库连接配置:jdbc.properties与MySQL 8.0时区参数
构建通过不代表能连上数据库。找到src/main/resources目录下的jdbc.properties(也有项目叫db.properties),这就是数据库连接的唯一配置入口。改造前的典型内容是把用户名密码改成自己的:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/nursing_home?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你自己的数据库密码如果本机MySQL是5.7或更早,驱动类名可以保持com.mysql.jdbc.Driver;如果是8.0,必须用com.mysql.cj.jdbc.Driver,否则启动直接报ClassNotFoundException。url里的参数逐一说清:useUnicode=true和characterEncoding=UTF-8保证中文正常读写;useSSL=false是跳过SSL握手,本地开发必须加,否则会有大量告警日志;serverTimezone=Asia/Shanghai是MySQL 8.0的必填参数,不加会报“The server time zone value is unrecognized”的异常。注意url里的“&”符号在XML里不需要转义,因为jdbc.properties不是XML文件,但如果你把这个配置写进applicationContext.xml里,就需要写成&。
改完配置后一定要重启Tomcat让配置重新加载,光刷新浏览器没有用。连接信息是否正确,可以先用命令行mysql -u root -p nursing_home试一下能不能登录,能登录再排查代码层面的问题。
4.4 配置Tomcat并首次启动:从部署到看到登录页的完整操作
在IDE里配置Tomcat的步骤是固定的:打开Run/Debug Configurations,新增Tomcat Server下的Local,在Server标签页选本机的Tomcat安装目录;切到Deployment标签页,点加号添加Artifact,选项目的war exploded包,Application context填写项目访问路径,一般保持根路径“/”或者项目名。这里有个小习惯:war exploded是未压缩的部署方式,启动快,改JSP可以直接刷新看到效果,开发阶段比war包方便得多。
启动时观察控制台日志,出现这些关键行说明启动正常:“Initializing Spring root WebApplicationContext”表示Spring容器加载完成,“Tomcat started on port(s): 8080”表示端口就绪。启动成功后打开浏览器访问http://localhost:8080/项目名/,应该能看到登录页。此时不知道账号密码怎么办?最常见的做法是查数据库里admin表或user表的前几行数据,初始密码如果看着像密文,就去源码里找登录逻辑对应的SQL,看它查的是哪张表哪个字段,直接对照着验证。这是所有接手源码包的人都应该掌握的第一步:不猜,去数据里找答案。
5. 部署与运行避坑:SSM养老院项目最常见的翻车现场与排查路径
这一章是血泪经验。SSM项目跑不起来的原因高度重复,每条下面按“现象→原因→解决”写清楚,遇到问题可以直接对照排查。
5.1 数据库连不上:驱动、时区、账号密码三类原因逐个排除
现象:启动时控制台报Cannot create PoolableConnectionFactory,或java.sql.SQLException: Access denied for user 'root'@'localhost'。
原因基本逃不出三类:一是MySQL服务没启动,或端口不是默认的3306;二是账号密码不对,或MySQL 8.0的root账号用了新的密码加密插件而驱动版本太老;三是连接串里的库名和实际库名不一致。先不碰代码,用命令行mysql -u root -p直接登录验证,能进去说明账号密码没问题,问题在url;进不去则先解决账号密码。驱动类是另一个常见雷区:MySQL 8.0必须配com.mysql.cj.jdbc.Driver,5.x配com.mysql.jdbc.Driver,交叉使用必报错。解决方法是按4.3节的配置逐项核对,改完重启Tomcat。
5.2 启动即报类找不到:依赖没下全与版本冲突的处理
现象:控制台抛ClassNotFoundException或NoClassDefFoundError,指向org.springframework或org.mybatis相关的类。
原因:Maven依赖下载中断、本地仓库里残留了损坏的jar、同一个依赖存在多个版本导致冲突。这种情况在刚换电脑或换网络环境时最常出现。解决路径就三步:先在项目根目录执行mvn clean install看原生报错;然后让IDE重新导入Maven工程;最后还不行,找到本地Maven仓库里对应的目录删掉,重新下载。多数情况下重下依赖就能解决,这不是代码问题,是环境问题。不要一上来就改pom.xml,那往往是误判。
5.3 接口路由全部404:url-pattern与组件扫描两处检查
现象:首页index.jsp能打开,但点击导航栏里任何功能都跳404,控制台没有任何异常日志。
原因排查两处。第一处在web.xml,DispatcherServlet的url-pattern如果写成了/*,JSP请求也被拦截,所有视图转发都会404,改成“/”即可。第二处在spring-mvc.xml,component-scan的base-package如果没覆盖到controller包,或Controller类上没加@Controller注解,DispatcherServlet根本找不到处理器。排查顺序是:先看web.xml的拦截规则,再看扫描包名和注解,最后看Controller里@RequestMapping的路径和前端请求路径是否一致。这类问题改完配置要重启并清掉Tomcat的work缓存目录,避免旧编译产物干扰。
5.4 中文乱码最后一公里:页面、请求、存储三个编码开关
现象:页面中文显示成问号;数据库里存的中文变成乱码;从页面提交的中文存进库里是乱码。
乱码是SSM项目里最折磨人的问题,因为它可能同时涉及三个环节:JSP页面本身的编码、Tomcat接收请求时解码用的编码、数据库连接和表结构的字符集。任何一处不一致,中文就保不住。逐项排查:确认JSP头部有pageEncoding="UTF-8";确认Tomcat的server.xml里Connector标签加了URIEncoding="UTF-8";确认jdbc连接串带characterEncoding=UTF-8;确认建表时指定了DEFAULT CHARSET=utf8mb4;最后在MySQL客户端执行SHOW VARIABLES LIKE 'character_set%'看服务端默认字符集。这些全部对齐后,把已有乱码数据删掉重新导入,乱码问题才彻底解决——对已存坏的数据做转换是更大的坑。
5.5 MyBatis绑定失败:namespace、资源打包、方法id三处核对
现象:调用Mapper接口方法时抛BindingException: Invalid bound statement (not found),后面跟着接口的全限定名和方法名。
原因有三个高频点。一是XML的namespace写错了,没等于接口全限定名,这是最基础的绑定规则。二是Mapper XML文件没有被打进classes目录——如果XML放在src/main/java的包目录下,Maven默认不会把它当资源文件打包,需要把XML移到src/main/resources下的mapper目录,或在pom.xml里配置resource。三是方法的id和XML里的statement id不一致,常见于改了方法名但忘了同步XML。解决:先核对namespace,再确认classes目录里能找到对应的XML文件,最后逐条比对方法名。有一个检查技巧:启动时看日志里有没有“Bean 'xxxMapper' of type”和“Class 'com.xxx.mapper.ElderMapper'”的注册信息,能确认接口有没有被Spring扫描到。
5.6 静态资源404与视图找不到:拦截规则和视图解析器路径
现象:页面HTML结构正常,但CSS、JS、图片全部404;或者Controller正常返回了字符串,但浏览器显示404/500。
静态资源404的原因上面提过,DispatcherServlet拦截了所有路径,spring-mvc.xml里缺了mvc:resources映射。把/static/**(前端资源所在路径)显式放行即可。视图找不到的原因则在InternalResourceViewResolver:prefix配置的/WEB-INF/jsp/与实际目录不一致,或返回字符串里多写了后缀,比如return "elder/list.jsp",视图解析器拼完变成elder/list.jsp.jsp。解决思路是确认JSP文件的实际物理路径,用文件系统路径反推配置应该怎么写,不要凭记忆猜。
5.7 端口被占用与残留进程:启动前的三秒检查习惯
现象:启动Tomcat时立刻报Port 8080 required by Tomcat v9.0 Server ... is already in use。
原因:上一次Tomcat没关干净,或者其他程序占了8080端口。很多开发者遇到过:IDE里点了Stop,但Java进程还在后台挂着,再次启动必然冲突。解决方法是先查端口再杀进程:
# Windows netstat -ano | findstr 8080 taskkill /PID 进程号 /F # Linux / macOS lsof -i:8080 kill -9 端口对应的PID我的习惯是每次启动前先确认上次的Tomcat确实关了,再点运行按钮。热部署调试时,改完代码先执行clean再重启,能省下很多“改了没生效”的排查时间。
6. 答辩展示与扩展改造:让这套智慧养老院系统比源码包多走一步
6.1 五步演示主线:把登录到费用结算串成连贯业务故事
答辩演示不要零散地打开每一个菜单,而是串一条主线:登录系统→新增一位老人并分配床位→给这位老人录入一条健康记录和一条用药记录→模拟家属来访登记→最后生成费用账单。这条主线把主数据表、过程记录表、查询页面全部覆盖,评委跟着走一遍就对系统有了整体印象。每一步操作前提前说一句“这里是做什么的”,比沉默点鼠标效果好得多。
6.2 低成本高展示度的扩展:用药提醒算是个不错的切入点
想在源码包基础上加功能,推荐“用药提醒”这类改动量小但业务价值明显的扩展。做法:给medication_record表加一个提醒时间字段,在项目首页加一个查询未来24小时待执行用药的接口,JSP页面上用颜色高亮即将到期的记录。改动只涉及一张表、一个Mapper方法、一个页面区块,但能讲清楚“我是从实际需求出发做了设计”,比简单加个统计图表更有说服力。
6.3 常见问法预判与最后一晚的自检习惯
答辩被问最频繁的三个技术点是:事务在哪个层控制、#{}和${}的区别、数据库表之间的关系。回答框架都在这篇笔记里了,关键是理解而不是背。我自己带毕业设计的习惯是:答辩前一晚不看PPT,而是把数据库每张表都SELECT *一遍,再从登录页把主流程走三遍。纸上的架构图是给别人看的,代码里的数据流才是自己的底气。希望帮到你。
本文还有配套的精品资源,点击获取