1. 项目概述与受众
先聊聊这套 "Java Spring Boot 基于 Android 的房屋租赁系统"。很多人一听这名字,第一反应是 "又一个毕设项目",但实际把它拆开看,它就是一套典型的移动端 + 服务端两层结构:Android 负责给租客、房东、管理员提供操作界面,Spring Boot 负责提供房源数据的增删改查、用户登录、租赁订单管理等所有后端能力。市面上大量房屋租赁平台,比如链家、自如的 App,核心流程也是这套逻辑:浏览房源、预约看房、下单签约、管理房源状态,区别只是业务复杂度和 UI 精致度。所以这个项目学通了,不只是能过答辩,而是能理解一个真实业务系统从数据库到接口再到 App 界面的完整链路。
这个系统能解决什么问题?最直白地讲,它把在线下中介门店或者社区公告栏里贴房源信息、打电话约看房的场景,搬到了手机端。房东在 App 里录入房源、传图片、设置租金;租客按区域、户型、租金区间筛选房源,看到合适的可以直接收藏或发起预约;管理员在后端接口的帮助下做房源审核、用户管理、数据统计。用技术来替代人工撮合和信息登记,这就是项目存在的意义。
适合什么人参考?如果你是做 Java 后端开发,想看看 Spring Boot 接口设计、权限认证、文件上传怎么在一个真实项目里落地;如果你是学 Android 的,想知道一个完整 App 怎么组织页面、怎么封装网络请求、怎么和后台联调;如果你是毕设或者课程设计,需要一套能讲清楚、能跑得起来的系统,那这个项目都值得花时间拆一遍。我这个分享不会只停留在 "下载源码、启动运行" 的层面,而是把里面的技术点、设计取舍、常见坑位全部讲透,看完之后你能对这套系统 "不仅会跑,还知道为什么这样写"。
2. 技术选型与架构思路
2.1 为什么选择 Spring Boot + Android
先说服务端。Spring Boot 在这个项目里的地位几乎是不可替代的。相对传统 SSM 框架,Spring Boot 内置 Tomcat,省掉大量 XML 配置,利用自动装配让开发者专注写业务代码。即便是刚从 Java Web 基础转过来的学生,也能在一两小时内把它跑起来。如果选 PHP 或 Node.js,当然也能做租赁系统,但在 Java 技术栈的分子里,Spring Boot 是面试、毕设、小公司外包项目中出场率最高的,学一遍的性价比极高。
Android 客户端为什么不用 Web 页面替代?因为项目的定位是 "移动端房屋租赁",并且 Android 原生能调用相册选图、推送本地通知、保持登录状态等能力,比 H5 页面体验更接近真实产品。在实际开发中,很多团队为了省事会把 Android 换成 Vue 做后台管理页面,再套一层 WebView,但这样一来你失去了原生 App 的完整开发链路——OkHttp 网络请求、RecyclerView 列表复用、图片框架加载,这些都是 Android 面试和工作中非常常用的点。这个项目选择原生 Android,走的是更正统且更耐看的路线。
2.2 前后端职责划分与交互流程
这套系统的架构并不复杂,但我建议学习的时候把它想象成 "一个前台 App + 一个后台服务"。后端 Spring Boot 不负责渲染任何页面,而是暴露一套 RESTful API,统一返回 JSON 数据。Android 端通过 HTTP 请求访问这些接口,拿到 JSON 后解析成 JavaBean,渲染到界面。这样前后端完全是解耦的,后期想换一个客户端,比如再加一个 iOS 端,后端代码一行都不用动。
典型的一次 "房东发布房源" 流程是这样的:
- Android 端登录,保存 Token 到本地。
- 房东在发布页面填写房源的标题、户型、面积、租金、地址、描述,并选择多张图片。
- Android 端把图片通过 OkHttp 以 multipart/form-data 方式上传到 Spring Boot 的文件上传接口。
- 接口返回图片 URL 后,Android 端再连同房源文本字段一起提交到
/house/add接口。 - Spring Boot 收到请求后先校验 Token 权限,再校验参数完整性,最后向 MySQL 中插入房源记录。
这个过程中客户端只做 "展示和收集",服务端做 "校验和落库",双方通过 JSON 定好数据结构。理解了这个分界线,后面看代码、改功能才有方向感。
2.3 基础设施与目录结构
我用一个表格把项目推荐的技术栈和版本整理出来,这套组合比较稳,不容易出现环境冲突:
| 层级 | 技术选型 | 备注 |
|---|---|---|
| 服务端框架 | Spring Boot 2.7.x | 兼容性最好,JDK 8/11 都能用 |
| ORM | MyBatis-Plus | 租户隔离、分页插件、CRUD 代码生成器很实用 |
| 数据库 | MySQL 5.7 / 8.0 | 推荐 5.7,多数毕设环境已预装 |
| 认证方案 | JWT(jsonwebtoken 0.9.1) | 无状态,Android 端存入本地 |
| Android 网络 | OkHttp 4.x + Gson | 简洁直接,便于理解请求流程 |
| Android 图片 | Glide 4.x | 图片加载缓存统一处理 |
| 图片存储 | 本地磁盘路径 + 静态资源映射 | 简单易部署,无需引入第三方 OSS |
在源码结构上,后端通常按照controller / service / mapper / entity / common / config分包。Android 端则是activity / adapter / bean / api / utils / fragment分层。这种分法不花哨,但胜在清晰。实际接手项目时,我第一件事就是把包结构看一遍,因为包结构能直接反映这个项目写没写 "人样"。
3. 数据库设计与核心表结构
3.1 用户、房源、订单、收藏表设计
数据库设计是决定系统好改难改的关键。很多毕设项目外观能跑,但新增一个功能要动六七张表,就是因为当初字段设计不合理。这套房屋租赁系统里,最核心的是四张表:user(用户),house(房源),housestate(租赁订单/状态),collect(收藏)。
用户表不必多说,需要区分管理员和普通用户,我一般在role字段里用数字区分:1表示管理员,2表示房东,3表示租客。有同学问为什么不建三张用户表,这属于过度设计。除非业务差异大到字段完全不同,否则一张表加角色字段就够了。
房源表是信息密度最大的表,我列出比较常见的字段设计思路,你可以直接抄作业:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键 |
| user_id | int | 发布者 ID |
| title | varchar(100) | 房源标题 |
| type | varchar(20) | 出租方式:整租/合租 |
| address | varchar(255) | 小区地址 |
| area | double | 面积(平方米) |
| price | double | 月租价格 |
| images | text | 图片 URL,多张用逗号分隔 |
| status | int | 0 待审核 / 1 已上架 / 2 已出租 / 3 已下架 |
| create_time | datetime | 发布时间 |
我这里特意把images字段设计成逗号分隔的字符串,而不是单独建一张图片表。有人会争论这样做不规范,但在这种小体量系统里,这是一种性价比极高的方案:查询列表时只需查一张表,减少一次子查询或关联查询。如果你追求范式严格,也可以拆图片表,但实际开发和毕设维护都会变麻烦,没必要。
订单表是整个业务核心,它负责记录一次租赁关系的完整状态变化,字段一般包括图片、house_id、user_id、landlord_id、start_time、end_time、rent_month 等,尤其是status字段要灵活动态化,因为后续接口逻辑全靠它判断。
3.2 状态字段与关键索引设计
状态字段是最容易被小看的。比如房源状态,我建议用int而不是字符串,原因很简单:字符串一旦拼写不一致,比如 "上架" 和 "上架 ",直接会导致列表查不到数据;数字枚举在代码里写常量或枚举类,可读性一点不差。后续你写 SQL,比如 "查询所有已上架房源",就是WHERE status = 1,清晰高效。
订单表的状态流转,我习惯用状态机思维设计,避免业务逻辑到处乱写。比如一次租赁订单可以经历这些状态:
- 0:待确认
- 1:已确认(等待支付/签约)
- 2:租赁中
- 3:已到期
- 4:已取消
- 5:已退租
这样一来,每次状态变化的判定都集中到service层的一个方法里,不会出现 "在 controller 里偷偷改字段" 的烂代码。
索引设计也不能只靠默认主键。在这个项目里,有两个查询场景频率最高:用户查看自己的发布记录(user_id查询)、平台端按状态筛选列表(status查询)。所以我会在user_id和status上各自加一个普通索引。数据量不大时可能看不出差别,但这是好习惯,也方便你在答辩时说清楚 "为什么加索引"。
4. 后端接口与核心逻辑实现
4.1 登录认证与权限控制
登录认证是后端最重要的一块。这个项目常用 JWT 或者 Session,我建议优先使用 JWT,因为它是无状态的,Android 端拿到 Token 存本地即可,服务端升级扩容、横向扩展时没有 Session 同步问题。
我简单描述一下 JWT 的接入思路。用户调用/user/login提交用户名和密码,后端校验通过后用 secret key 生成 Token,返回给 Android。Token 里可以带上 userId、role 这些关键信息,但千万不要把密码放进去。之后每次 Android 发起需要身份认证的请求,都在 header 里携带token: xxx。Spring Boot 通过一个拦截器或者过滤器,在进入 controller 前解析 Token、校验合法性和有效期,再把用户信息放到 request 里。
写拦截器时有几个容易踩的坑:
- 不需要认证的路径,比如登录、注册、房源列表,必须放行,否则用户没登录就看不到任何内容。
- Token 过期后要返回统一格式的错误信息,Android 端收到后需要做 "重新登录" 的跳转提示,而不是让界面卡在空白页面。
- 管理员的接口要校验 role 字段,防止普通用户通过直接调用接口删房源。
4.2 租赁流程中的关键接口与状态机
围绕房屋租赁,我认为最值得学习的是两个接口:添加房源和创建订单。
添加房源接口的流程我前面提过,这里补充一个关键点:图片上传接口一定不能和房源新增接口耦合成一个接口,否则图片较大时请求超时概率很高。正确做法是先后端提供一个/upload返回图片 URL,再把 URL 拼接进房源数据。这样一方面提高了重试的灵活性,另一方面方便复用。上传建议限制图片大小,比如单张最大 5MB,格式校验白名单,防止用户传非图片文件进来。
创建订单接口的难点在于并发和防重复。业务中租客可能手快点了两次 "立即租房",如果不做处理,会出现同一房源同时生成两笔订单。处理方法不复杂:在创建订单前,先查询该房源当前状态是否为 "已上架",如果不是就返回明确提示;同时给用户 ID 加一个防重复提交标记,比如 Redis 里面存一个 key,这是企业级做法。但如果你不想引入 Redis,也可以在 SQL 层面做判断,用一个带house_id + status的唯一约束兜底,让数据库去挡第二次提交。
下面这段代码可以帮你理解查询房源列表时常用的条件组合写法:
public IPage<House> getHouseList(int page, int size, String type, Integer status, String keyword) { LambdaQueryWrapper<House> wrapper = new LambdaQueryWrapper<>(); if (StrUtil.isNotBlank(type)) { wrapper.eq(House::getType, type); } if (status != null) { wrapper.eq(House::getStatus, status); } if (StrUtil.isNotBlank(keyword)) { wrapper.like(House::getTitle, keyword); } wrapper.orderByDesc(House::getCreateTime); return houseMapper.selectPage(new Page<>(page, size), wrapper); }这里我习惯用LambdaQueryWrapper,好处是字段名是带编译时检查的,字段拼错会直接报错,而不是运行时才暴露问题。分页用的是 MyBatis-Plus 自带的分页插件,前端传page和size,后端返回total和records,这套交互协议在真实项目里也是标准做法。
4.3 参数校验与统一返回格式
很多新手项目最大的问题是前后端各写各的,字段名对不上,错误提示对不上。要避免这个问题,从第一天就要定好统一返回结构。我常用这样的 JSON 格式:
{ "code": 0, "message": "success", "data": {} }code=0表示成功,非 0 表示失败。message是给用户看的提示,比如 "用户名或密码错误"、"图片大小不能超过5MB"。Android 端解析的时候只用判断code是否为 0,再拿到data来做后续逻辑,非常省事。
参数校验要依托@Validated注解加实体类字段约束,比如@NotBlank、@NotNull、@Min。千万别把一堆 if 判断写在 controller 里,谁维护谁知道痛苦。当然,校验不通过的异常也要集中处理,用@RestControllerAdvice + @ExceptionHandler把异常统一包装成上面的 JSON 格式,再返回给 Android 端。这一块虽然在界面上看不到,但在后端代码评审时加分极多。
5. Android 端实现细节
5.1 项目结构与网络层搭建
Android 端的源码质量直接决定这个项目给人第一印象好不好。我最反感的是把几百行代码全部写在 MainActivity 里,页面一多就变成 "面条代码"。这个项目我建议至少分这么几类包:
bean:存放对应后端返回结构的实体类。api:存放接口定义,比如登录接口、房源列表接口、收藏接口。adapter:各个列表的适配器。activity/fragment:页面逻辑。utils:公共工具类,PrefUtil、网络判断等。
网络层不用过度封装,但至少要做一个单例。我讲解视频里最常演示的是用 OkHttp 封装一个HttpUtil类,内部维护一个 OkHttpClient 实例,对外提供 get 和 post 方法。再在此基础上加一个回调接口,把成功和失败分发出去,这样 Activity 里就不用关心线程切换问题了。
HttpUtil.post("house/list", params, new HttpUtil.CallBack() { @Override public void onSuccess(String json) { // 解析 json,更新 UI } @Override public void onFailed(String msg) { Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } });回调里要记得runOnUiThread切换主线程,否则直接更新 TextView 会崩溃。这个细节很多初学者会忽略,也是很常见的 Android 崩溃点。
5.2 页面清单与功能拆解
在界面设计上,一个完整租赁 App 至少要包含这些页面:
- 登录、注册页。
- 主界面:首页(房源列表或轮播推荐)、类型筛选、个人中心,用 BottomNavigationView + Fragment 实现。
- 房源详情页:图片轮播、房屋信息、房东信息、收藏按钮、预约看房/立即租房按钮。
- 发布房源页:表单录入 + 多图选择上传。
- 我的订单页:区分 "我发布的" 和 "我租赁的"。
- 后台管理页:房源审核、用户列表、数据概览。
房源列表页通常用 RecyclerView 展示,Adapter 里的 item 布局要包含标题、价格、户型、面积、封面图。这里有一个实际经验:列表页不要一次性把所有字段都加载进来,尤其是images字段较长,很容易拖慢首次加载速度。更合理的做法是在列表接口中只返回图片的第一张,也就是前端截取逗号分隔结果的第一个,这样列表加载会明显变快。
图片加载直接上 Glide。Glide 的好处是支持占位图、错误图、内存和磁盘缓存,还能自动处理 OOM 风险。如果你还在用它加载网络图片时忘记加路径,比如把http://192.168.1.100:8080/upload/1.png写成本地路径,那一定加载不出来。
5.3 登录状态与 Token 管理
Android 端的登录态保存,最简单可靠的是 SharedPreferences。登录成功以后,把 Token、userId、role 存下来;在每次网络请求的 header 里带上 Token;用户在个人中心点击退出登录时,清除这些数据并跳回登录页。
这个流程看起来简单,实际有几个细节要注意。第一,SharedPreferences 提交要用apply()而不是commit(),因为前者是异步写入,不会卡主线程。第二,不能把所有页面都要求登录,比如首页房源列表一定要允许未登录访问,否则搜索引擎和分享链接的场景会非常尴尬。第三,当某个接口返回 Token 失效时,Android 端最好能统一弹出一个对话框让用户重新登录,而不是在每个 Activity 里各自判断。可以用一个全局的 Activity 管理工具,在检测到失效码时关闭所有页面并回到登录页,这样体验才像一个真正的商业 App。
6. 运行部署与调试经验
6.1 本地启动步骤
一个新环境要跑起这个系统,顺序很重要。先启动 MySQL,创建好数据库house_rental,修改后端配置里的数据库连接字符串,然后启动 Spring Boot 项目。建议在src/main/resources下的application.yml中用外置配置覆盖默认配置,这样换环境不用改源码。
你可以这样配置:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/house_rental?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 5MB max-request-size: 20MB mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl启动后后端默认端口 8080,可以用 Postman 或者直接用浏览器访问http://localhost:8080/house/list验证接口能否返回 JSON。我每次都建议新人先跑通一个最简接口再写页面,不要一上来就全量堆代码,否则定位问题会非常难。
Android 端先在 Android Studio 里导入工程,等待 Gradle 同步完成。注意本机 Android SDK 版本要匹配项目配置,最常见的报错是 SDK Build Tools 版本缺失,AS 一般会提示自动安装。然后在模拟器或真机上运行。用模拟器时,后端地址一定要写成http://10.0.2.2:8080,而不是localhost,因为模拟器里localhost指向模拟器自己。用真机时,填电脑的局域网 IP,并且保证手机和电脑连的是同一个 Wi-Fi。
6.2 常见联调问题与排查思路
联调阶段我整理了下面几个高频问题,几乎每个初学者都会遇到:
| 现象 | 原因 | 解决办法 |
|---|---|---|
| Android 请求接口超时 | 后端服务没启动,或地址填错 | 先看后端控制台日志,再确认 IP/端口 |
| 中文乱码 | 数据库字符集不是 utf8mb4 | 在建库语句里指定DEFAULT CHARSET=utf8mb4,同时连接串加characterEncoding=utf8 |
| 图片上传后加载 404 | 后端静态资源映射未配置 | 在 WebMvcConfig 中配置/upload/**资源映射到本地磁盘路径 |
| 房源列表一直加载失败 | JSON 字段名与 JavaBean 不对应 | 打印后端返回 JSON,检查 Bean 字段名,确认有无@SerializedName |
| 真机访问不了后端 | 手机与电脑不在同一局域网/防火墙拦截 | 关防火墙或放行 8080 端口,用别的手机热点测试 |
关于跨域,Android 原生 App 其实不受浏览器跨域限制,但会有明文流量限制。如果后端是 http 而 Android 较高版本默认禁止明文,你需要在AndroidManifest.xml里设置usesCleartextTraffic="true",不然请求会直接被拦截。这个问题特别隐蔽,我见过很多同学在模拟器上正常、在真机上挂了半天找不到原因。
7. 二次开发建议与避坑清单
7.1 新手最容易踩的坑
这个项目如果要作为毕设或者真实项目交付,有几个坑我真心建议避开。
第一个坑是时间字段的设计。很多新手会把starttime、endtime字段设计成 String,直接存 "2024-06-01" 这样的文本。这样做的危害是排序、比较租期时会出错,而且无法使用 MySQL 的时间函数。规范做法是用datetime类型,后端用LocalDateTime接收和返回,前端再格式化展示。
第二个坑是删除数据的粗暴操作。房源、订单这类数据,千万别用物理删除。一旦用户不小心误删,或者面试官问 "如何恢复数据",你就难堪了。建议加一个is_deleted字段做逻辑删除,MyBatis-Plus 也支持配置@TableLogic注解,查询时自动过滤,删除时自动改为 update,非常省心。
第三个坑是接口权限遗漏。很多毕设项目只在界面上做了按钮隐藏,但接口完全没有权限过滤,用户只要抓个包直接调用接口就能删别人房源。这个系统里至少要保证 "修改、删除、审核" 三类接口都必须校验管理员或资源拥有者身份。实现方式是在 service 里传入当前用户 ID,再和资源的user_id比对,不一致就直接抛出 "无权操作"。
7.2 可扩展方向
如果答辩时间充裕,你完全可以在基础功能上加几个亮点,成本和收益都高。
- 在地图上展示房源位置:后端存经纬度字段,Android 端引入高德或者百度地图 SDK,做一个房源地图视图。这个功能视觉冲击力强,且实现不复杂。
- 增加短信验证码登录:借助第三方短信服务,传递用户手机号和验证码,替换一部分密码登录场景,可以展示你对消息通信的理解。
- 增加爬虫或者数据字典:比如在后台统计各区域房源数、平均租金,用 ECharts 或 Android 图表框架展示。这是妥妥的加分项。
- 租约支付:如果要做得像商业产品,还需要抽象支付回调流程,但毕设阶段不一定落地,可以在文档里描述清楚设计思路。
另外提一句,如果你想把这个项目包装成简历项目,切记不能只写 "实现了房屋租赁 CRUD"。要突出你在权限控制、状态机设计、防重复提交、图片上传优化、真机联调兼容性这些点上的思考,这才是面试官想听的东西。
8. 实操体验与收尾建议
最后分享一点个人体会。带过几届学生做类似的毕设和训练营项目,我发现大家最容易卡住的阶段不是写代码,而是 "代码跑起来之后不知道干什么"。拿到这套系统源码后,我建议你按三步走:第一步,不看代码,把后端数据库建出来,用 Postman 测一遍主要接口,感受一下接口的请求和响应结构;第二步,对照源码把接口实现捋清楚,画一张自己看得懂的调用链路图;第三步,改一个小功能,比如给房源列表增加按价格排序的参数,亲自动手改代码、重启服务、验证效果。走完这三步,这套系统才真正变成了你的东西。
如果你打算在毕设答辩里讲这个项目,多准备几个 "为什么" 的回答。比如 "为什么订单状态用 int 不用 String"、"为什么图片地址用逗号拼接而不是关联表"、"为什么登录用 Token 不用 Session"。这些问题都是加分机会,也是真正体现你深入理解项目的时刻。
做这套系统,难度不在于堆功能,而在于把每个模块之间的边界打磨清楚。服务端只提供接口逻辑,客户端只负责展示和交互,数据库用合理字段支撑业务流转。这套思路放到任何业务系统里都适用。希望这篇分享能帮你少走一些弯路,把项目做得比大多数模板更有底气。