简介:这是一套面向高校计算机专业学生与Java初学者、可用于课程设计或毕业设计的房屋租赁系统完整资料,基于SpringBoot框架搭配JSP与MySQL开发,涵盖管理员、房东、用户三类角色的房源发布、信息审批、订单管理等核心业务。资源包共913个文件,约24.22MB,以162个Java源码、56个Vue组件、152个JavaScript脚本、56个HTML页面及44个CSS样式构成前后端主体,另含SQL建库脚本、SVG图标、图片素材与配置文件,并附论文文档与PPT答辩稿。已有63人学习下载。内容按绪论、开发环境与技术、系统分析、系统设计、系统实现、系统测试六章展开,从可行性分析、登录与增删改流程到数据库概念与物理设计均有说明,读者可据此掌握完整开发链路、理解分层结构与排错思路,并直接用于答辩与二次开发。
1. 从一份能跑通的 SpringBoot 房屋租赁系统说起:为什么毕设选它不容易翻车
每年到了毕设选题季,总有一批同学在“做个什么系统”这件事上反复横跳。我的建议一直很直接:如果你 Java 基础还行、想稳扎稳打拿一个能写进简历的项目,基于 SpringBoot 的房屋租赁系统是性价比极高的选择。它不像电商秒杀那样需要处理极端并发,也不像社交平台那样涉及复杂的推荐算法,业务边界清晰——房东发房源、租客看房下单、管理员审核,三条主线就能撑起一个完整的 CRUD 闭环。更关键的是,这套业务模型天然适合写论文:需求分析有据可依,E-R 图关系明确,测试用例也好设计。
但“能做”和“做好”之间隔着一条河。我见过太多人卡在几个地方:SpringBoot 版本选太高导致依赖冲突、文件上传没做安全过滤被 XSS 攻击、论文框架搭得松散被导师打回重写、答辩 PPT 堆满文字讲不到重点。这篇笔记就按“先跑通、再加固、最后能讲清楚”的顺序,把房屋租赁系统从技术选型到源码落地、从论文框架到答辩 PPT 的完整路径拆开讲。适合正在做毕设的本科生、需要交课程设计的研究生,也适合想拿一个完整 SpringBoot 项目练手的初级开发者。
2. 技术选型与骨架搭建:为什么这套组合拳最稳
2.1 后端为什么锁定 SpringBoot + MyBatis-Plus
选 SpringBoot 做房屋租赁系统的后端,核心理由是“约定大于配置”带来的启动效率。传统 SSM 要写一堆 XML 映射文件,光配置就耗掉两天,而 SpringBoot 内嵌 Tomcat,一个 main 方法就能把服务拉起来。配合 MyBatis-Plus,单表 CRUD 几乎不用写 SQL,分页插件、逻辑删除、自动填充这些常用能力开箱即用,能把精力集中在租赁业务本身。
版本选择上有个血泪经验:不要盲目追新。SpringBoot 3.x 要求 JDK 17 起步,很多学校的实验环境还停留在 JDK 8,强行上 3.x 会在打包和部署环节反复翻车。我一般推荐 SpringBoot 2.7.x 搭配 JDK 8 或 11,这个组合生态最成熟,遇到问题搜到的解决方案也最多。如果你确实想用 JDK 17,那就统一升到 SpringBoot 3.1.x,别混搭。
数据库用 MySQL 8.0,字符集选 utf8mb4,避免租客姓名或房源描述里的生僻字变成乱码。连接池用 HikariCP,SpringBoot 默认就带,不用额外引入。缓存层如果要做房源热度排行,可以加 Redis,但毕设阶段不是必须,先把核心业务跑通再说。
2.2 用 Spring Initializr 生成项目骨架
第一步不是急着写代码,而是把项目骨架搭对。我习惯用 Spring Initializr 生成基础结构,依赖勾选清楚,省得后面手动补。
# 用 curl 调 Spring Initializr 生成项目骨架 curl https://start.spring.io/starter.zip \ -d type=maven-project \ -d language=java \ -d bootVersion=2.7.18 \ -d groupId=com.example \ -d artifactId=house-rental \ -d name=house-rental \ -d packageName=com.example.houserental \ -d javaVersion=8 \ -d dependencies=web,mybatis-plus,mysql,lombok,validation \ -o house-rental.zip unzip house-rental.zip -d house-rental cd house-rental这段命令做了三件事:指定 Maven 构建、锁定 SpringBoot 2.7.18、一次性拉入 Web、MyBatis-Plus、MySQL 驱动、Lombok 和参数校验五个依赖。bootVersion这个参数是核心,写错版本后面改 pom.xml 会很麻烦。dependencies里没有加 Redis 和 Security,因为毕设阶段权限控制用拦截器加注解就够了,引入 Security 反而会让登录流程变复杂。
生成后先别动代码,跑一次mvn clean compile确认依赖能正常下载。如果卡在某个依赖上,大概率是 Maven 镜像源问题,换成阿里云镜像即可。
2.3 配置文件里必须改的四个参数
application.yml是项目的控制中枢,以下四个参数不改,项目跑不起来或者跑起来有隐患。
server: port: 8080 servlet: context-path: /rental spring: datasource: url: jdbc:mysql://localhost:3306/house_rental?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 5MB max-request-size: 20MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0context-path加上统一前缀,后面前端调接口和 Nginx 转发都好处理。serverTimezone必须显式指定,否则 MySQL 8 会报时区错误。max-file-size限制单文件 5MB,房源图片够用了,设太大容易被人上传大文件拖垮服务。map-underscore-to-camel-case打开后,数据库的create_time会自动映射到 Java 的createTime,省去手写 ResultMap。逻辑删除字段配好后,删除操作变成更新deleted=1,数据可追溯,答辩时也是个加分项。
3. 核心业务模块的落地:房源、订单、用户三条线怎么串
3.1 房源表设计与发布接口实现
房源是整个系统的核心实体,表结构设计直接影响后续查询效率。我一般把房源基础信息和房源图片分成两张表,一对多关系,避免图片多时主表字段膨胀。
CREATE TABLE `house` ( `id` bigint NOT NULL AUTO_INCREMENT, `landlord_id` bigint NOT NULL COMMENT '房东用户ID', `title` varchar(100) NOT NULL COMMENT '房源标题', `address` varchar(200) NOT NULL COMMENT '详细地址', `rent` decimal(10,2) NOT NULL COMMENT '月租金', `area` decimal(8,2) DEFAULT NULL COMMENT '面积', `room_type` varchar(20) DEFAULT NULL COMMENT '户型', `status` tinyint DEFAULT '0' COMMENT '0待审核 1已上架 2已下架', `deleted` tinyint DEFAULT '0', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_landlord` (`landlord_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;landlord_id和status都建了索引,因为租客端查询“已上架房源”和房东端查询“我的房源”是两个最高频的操作。rent用 decimal 而不是 float,金额计算不能有精度损失。status用 tinyint 而不是 varchar,省空间且查询快。
发布房源的接口用@PostMapping接收 JSON,配合@Valid做参数校验。
@RestController @RequestMapping("/api/house") public class HouseController { @Autowired private HouseService houseService; @PostMapping("/publish") public Result publish(@RequestBody @Valid HousePublishDTO dto, @RequestAttribute Long userId) { // userId 由登录拦截器注入,避免前端伪造房东身份 dto.setLandlordId(userId); houseService.publish(dto); return Result.success("发布成功,等待审核"); } }这里有个关键设计:landlordId不从请求体里取,而是从拦截器解析 token 后注入的userId拿。如果直接从 DTO 里读,恶意用户可以把landlordId改成别人的,把房源挂到别人名下。@Valid配合 DTO 上的@NotBlank、@Min注解,能在进入业务逻辑前拦掉大部分脏数据。
3.2 租赁订单的状态流转与防重复下单
订单模块的难点不在增删改查,而在状态流转和并发控制。一个订单从创建到完成,要经过“待确认→已确认→租赁中→已完成”四个状态,中间还可能插入“已取消”。状态不能乱跳,比如不能从“待确认”直接跳到“已完成”。
public enum OrderStatus { PENDING(0, "待确认"), CONFIRMED(1, "已确认"), RENTING(2, "租赁中"), FINISHED(3, "已完成"), CANCELLED(4, "已取消"); // 定义合法流转路径 private static final Map<OrderStatus, List<OrderStatus>> TRANSFER = new HashMap<>(); static { TRANSFER.put(PENDING, Arrays.asList(CONFIRMED, CANCELLED)); TRANSFER.put(CONFIRMED, Arrays.asList(RENTING, CANCELLED)); TRANSFER.put(RENTING, Arrays.asList(FINISHED)); TRANSFER.put(FINISHED, Collections.emptyList()); TRANSFER.put(CANCELLED, Collections.emptyList()); } public static boolean canTransfer(OrderStatus from, OrderStatus to) { return TRANSFER.getOrDefault(from, Collections.emptyList()).contains(to); } }用枚举加静态 Map 定义流转规则,比在 Service 里写一堆 if-else 清晰得多。每次状态变更前调canTransfer校验,非法流转直接抛业务异常。
防重复下单是另一个坑。同一个租客对同一套房源,在“待确认”状态下不能重复提交订单。做法是在订单表上加唯一索引uk_tenant_house_status,包含tenant_id、house_id和一个标记字段,或者在 Service 层用 Redis 分布式锁。毕设阶段用数据库唯一索引就够了,简单可靠。
ALTER TABLE `rental_order` ADD UNIQUE KEY `uk_tenant_house_active` (`tenant_id`, `house_id`, `active_flag`);active_flag在订单有效时为订单 ID,取消或完成后置为 0,这样同一个租客对同一房源同时只能有一条有效订单。
3.3 用户角色与登录拦截器的轻量实现
房屋租赁系统有三类角色:租客、房东、管理员。权限控制不需要上 Spring Security,一个基于 token 的拦截器就能搞定。
@Component public class AuthInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录和注册接口 String uri = request.getRequestURI(); if (uri.contains("/api/user/login") || uri.contains("/api/user/register")) { return true; } String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { response.setStatus(401); return false; } String userId = redisTemplate.opsForValue().get("token:" + token); if (userId == null) { response.setStatus(401); return false; } // 把 userId 注入请求属性,Controller 里直接取 request.setAttribute("userId", Long.valueOf(userId)); return true; } }登录成功后生成 UUID 作为 token,存 Redis 并设 30 分钟过期,同时返回给前端。前端每次请求把 token 放在Authorization头里。拦截器校验通过后把userId塞进 request 属性,Controller 用@RequestAttribute取,全程不碰 Session,天然支持前后端分离。角色校验可以在拦截器里再加一层,根据 URI 前缀判断需要的角色,比如/api/admin/**只允许管理员访问。
4. 避坑与排查:那些让项目跑不起来的常见问题
4.1 启动报错“Failed to configure a DataSource”
现象:项目一启动就抛Failed to configure a DataSource: 'url' attribute is not specified,连 Tomcat 都没起来。
原因:SpringBoot 自动配置检测到 classpath 下有 MySQL 驱动,但application.yml里没配spring.datasource.url,或者配置文件没被加载到。常见于把配置写在application.properties里但文件名拼错,或者多环境配置没激活正确的 profile。
解决:先确认src/main/resources下配置文件名称正确,然后检查spring.datasource下四个必填项是否齐全。如果用了多环境,在application.yml里加spring.profiles.active=dev并确保application-dev.yml存在。实在找不到原因,在启动类上加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)先排除自动配置,看是否能启动,再逐步排查。
4.2 文件上传接口被 XSS 攻击
现象:房源图片上传接口上线后,有人上传了一个文件名带<script>标签的文件,前端展示文件名时弹出了脚本。
原因:上传接口只校验了文件后缀,没有对文件名做过滤,前端又把原始文件名直接渲染到页面上。这是典型的存储型 XSS。
解决:后端接收文件后,立即用 UUID 重命名,丢弃原始文件名,只保留后缀。同时校验文件魔数,防止改后缀绕过。
public String upload(MultipartFile file) { // 校验真实文件类型,不信任后缀 String originalName = file.getOriginalFilename(); String suffix = originalName.substring(originalName.lastIndexOf(".")); if (!Arrays.asList(".jpg", ".jpeg", ".png").contains(suffix.toLowerCase())) { throw new BizException("只支持 jpg/png 格式"); } // UUID 重命名,彻底丢弃原始文件名 String newName = UUID.randomUUID().toString().replace("-", "") + suffix; // 存储路径按日期分目录,避免单目录文件过多 String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")); File dest = new File(uploadDir + datePath + "/" + newName); dest.getParentFile().mkdirs(); file.transferTo(dest); return "/upload/" + datePath + "/" + newName; }前端展示时只显示“图片”或缩略图,不显示原始文件名。如果业务需要展示文件名,用textContent而不是innerHTML赋值。
4.3 MyBatis-Plus 分页插件不生效
现象:调分页接口,返回的数据是全量列表,total也不对。
原因:MyBatis-Plus 的分页功能依赖PaginationInnerInterceptor拦截器,如果只引入了依赖但没注册这个 Bean,分页参数会被忽略。
解决:在配置类里显式注册拦截器。
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 指定数据库类型为 MySQL interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }注意DbType.MYSQL不能省,否则分页 SQL 的方言可能不对。如果项目里同时用了多数据源,每个数据源都要单独配。
4.4 论文查重率过高被导师打回
现象:论文初稿提交后查重率 40% 以上,标红的大段是需求分析和数据库设计部分。
原因:需求分析直接抄了网上模板,E-R 图描述和表结构说明用了通用话术,没有结合自己系统的实际业务。
解决:需求分析部分用自己的话重写,把“用户管理模块”改成“租客注册后可以浏览房源、提交租赁申请,房东登录后可以发布房源、确认订单”,每句话都对应到具体功能。数据库设计部分不要只贴建表语句,要解释每个字段为什么这么设计,比如“rent 字段用 decimal 是因为金额计算不能有浮点误差”。查重系统对代码和 SQL 的识别较弱,但连续 13 个字相同的自然语言就会被标红,所以描述性文字必须改写。
4.5 答辩 PPT 被评委说“像产品说明书”
现象:PPT 每页都是功能截图加文字说明,讲了十分钟评委还在问“你的技术难点在哪”。
原因:PPT 结构按功能模块罗列,没有突出技术选型理由和解决的问题。
解决:把 PPT 结构改成“问题→方案→效果”三段式。第一页讲房屋租赁场景下信息不对称、订单状态混乱的问题;第二页讲为什么选 SpringBoot 加 MyBatis-Plus,解决了什么具体问题;第三页展示核心代码片段和状态流转图;最后用测试数据说明系统能支撑多少并发、响应时间多少。功能截图放附录,正文只放架构图和关键流程。
5. 论文框架与答辩 PPT 的收尾技巧:让评委看到你的思考
论文写到最后一章,很多人习惯性开始堆功能列表,这是大忌。评委想看到的是你对系统的理解深度,而不是你做了多少个页面。我的习惯是:论文最后一章放“系统测试与性能分析”,用真实数据说话。比如用 JMeter 对房源查询接口做 100 并发压测,记录平均响应时间和吞吐量,再和优化前的数据对比。哪怕数据不漂亮,只要分析清楚瓶颈在哪、下一步怎么优化,就是加分项。
答辩 PPT 控制在 12 页以内,结构我一般这样排:第 1 页封面,第 2 页选题背景与问题,第 3 页技术选型对比表,第 4 页系统架构图,第 5 页数据库 E-R 图,第 6 页核心业务流程图,第 7 页关键代码片段,第 8 页测试数据与性能分析,第 9 页不足与改进方向,第 10 页致谢。每页只讲一个重点,讲的时候用“我遇到了什么问题、怎么解决的、效果如何”这条线串起来。
有个细节容易被忽略:PPT 里的代码片段不要直接截图 IDE,背景色和字体在投影仪上往往看不清。用 Carbon 或类似工具把代码渲染成高对比度的图片,字号至少 18pt。数据库表结构用表格展示,字段名、类型、说明三列,比贴建表语句直观得多。
最后说一个我自己的教训:第一次做毕设答辩时,我把所有功能都讲了一遍,结果评委问“你这个系统和网上的开源项目有什么区别”,我答不上来。后来我学乖了,在 PPT 里专门加了一页“本系统的差异化设计”,写清楚我在订单状态流转上加了合法性校验、在文件上传上做了 XSS 过滤、在分页查询上做了索引优化。这些点不大,但能证明你是真的动手做了,而不是下载了一个源码改了个名字。希望帮到你。
本文还有配套的精品资源,点击获取