實戰(zhàn):從架構設計到聯(lián)調部署)
1. 項目概述與受眾先聊聊這套 Java Spring Boot 基于 Android 的房屋租賃系統(tǒng)。很多人一聽這名字第一反應是 又一個畢設項目但實際把它拆開看它就是一套典型的移動端 服務端兩層結構Android 負責給租客、房東、管理員提供操作界面Spring Boot 負責提供房源數(shù)據(jù)的增刪改查、用戶登錄、租賃訂單管理等所有后端能力。市面上大量房屋租賃平臺比如鏈家、自如的 App核心流程也是這套邏輯瀏覽房源、預約看房、下單簽約、管理房源狀態(tài)區(qū)別只是業(yè)務復雜度和 UI 精致度。所以這個項目學通了不只是能過答辯而是能理解一個真實業(yè)務系統(tǒng)從數(shù)據(jù)庫到接口再到 App 界面的完整鏈路。這個系統(tǒng)能解決什么問題最直白地講它把在線下中介門店或者社區(qū)公告欄里貼房源信息、打電話約看房的場景搬到了手機端。房東在 App 里錄入房源、傳圖片、設置租金租客按區(qū)域、戶型、租金區(qū)間篩選房源看到合適的可以直接收藏或發(fā)起預約管理員在后端接口的幫助下做房源審核、用戶管理、數(shù)據(jù)統(tǒng)計。用技術來替代人工撮合和信息登記這就是項目存在的意義。適合什么人參考如果你是做 Java 后端開發(fā)想看看 Spring Boot 接口設計、權限認證、文件上傳怎么在一個真實項目里落地如果你是學 Android 的想知道一個完整 App 怎么組織頁面、怎么封裝網(wǎng)絡請求、怎么和后臺聯(lián)調如果你是畢設或者課程設計需要一套能講清楚、能跑得起來的系統(tǒng)那這個項目都值得花時間拆一遍。我這個分享不會只停留在 下載源碼、啟動運行 的層面而是把里面的技術點、設計取舍、常見坑位全部講透看完之后你能對這套系統(tǒng) 不僅會跑還知道為什么這樣寫。2. 技術選型與架構思路2.1 為什么選擇 Spring Boot Android先說服務端。Spring Boot 在這個項目里的地位幾乎是不可替代的。相對傳統(tǒng) SSM 框架Spring Boot 內置 Tomcat省掉大量 XML 配置利用自動裝配讓開發(fā)者專注寫業(yè)務代碼。即便是剛從 Java Web 基礎轉過來的學生也能在一兩小時內把它跑起來。如果選 PHP 或 Node.js當然也能做租賃系統(tǒng)但在 Java 技術棧的分子里Spring Boot 是面試、畢設、小公司外包項目中出場率最高的學一遍的性價比極高。Android 客戶端為什么不用 Web 頁面替代因為項目的定位是 移動端房屋租賃并且 Android 原生能調用相冊選圖、推送本地通知、保持登錄狀態(tài)等能力比 H5 頁面體驗更接近真實產品。在實際開發(fā)中很多團隊為了省事會把 Android 換成 Vue 做后臺管理頁面再套一層 WebView但這樣一來你失去了原生 App 的完整開發(fā)鏈路——OkHttp 網(wǎng)絡請求、RecyclerView 列表復用、圖片框架加載這些都是 Android 面試和工作中非常常用的點。這個項目選擇原生 Android走的是更正統(tǒng)且更耐看的路線。2.2 前后端職責劃分與交互流程這套系統(tǒng)的架構并不復雜但我建議學習的時候把它想象成 一個前臺 App 一個后臺服務。后端 Spring Boot 不負責渲染任何頁面而是暴露一套 RESTful API統(tǒng)一返回 JSON 數(shù)據(jù)。Android 端通過 HTTP 請求訪問這些接口拿到 JSON 后解析成 JavaBean渲染到界面。這樣前后端完全是解耦的后期想換一個客戶端比如再加一個 iOS 端后端代碼一行都不用動。典型的一次 房東發(fā)布房源 流程是這樣的Android 端登錄保存 Token 到本地。房東在發(fā)布頁面填寫房源的標題、戶型、面積、租金、地址、描述并選擇多張圖片。Android 端把圖片通過 OkHttp 以 multipart/form-data 方式上傳到 Spring Boot 的文件上傳接口。接口返回圖片 URL 后Android 端再連同房源文本字段一起提交到/house/add接口。Spring Boot 收到請求后先校驗 Token 權限再校驗參數(shù)完整性最后向 MySQL 中插入房源記錄。這個過程中客戶端只做 展示和收集服務端做 校驗和落庫雙方通過 JSON 定好數(shù)據(jù)結構。理解了這個分界線后面看代碼、改功能才有方向感。2.3 基礎設施與目錄結構我用一個表格把項目推薦的技術棧和版本整理出來這套組合比較穩(wěn)不容易出現(xiàn)環(huán)境沖突層級技術選型備注服務端框架Spring Boot 2.7.x兼容性最好JDK 8/11 都能用ORMMyBatis-Plus租戶隔離、分頁插件、CRUD 代碼生成器很實用數(shù)據(jù)庫MySQL 5.7 / 8.0推薦 5.7多數(shù)畢設環(huán)境已預裝認證方案JWTjsonwebtoken 0.9.1無狀態(tài)Android 端存入本地Android 網(wǎng)絡OkHttp 4.x Gson簡潔直接便于理解請求流程Android 圖片Glide 4.x圖片加載緩存統(tǒng)一處理圖片存儲本地磁盤路徑 靜態(tài)資源映射簡單易部署無需引入第三方 OSS在源碼結構上后端通常按照controller / service / mapper / entity / common / config分包。Android 端則是activity / adapter / bean / api / utils / fragment分層。這種分法不花哨但勝在清晰。實際接手項目時我第一件事就是把包結構看一遍因為包結構能直接反映這個項目寫沒寫 人樣。3. 數(shù)據(jù)庫設計與核心表結構3.1 用戶、房源、訂單、收藏表設計數(shù)據(jù)庫設計是決定系統(tǒng)好改難改的關鍵。很多畢設項目外觀能跑但新增一個功能要動六七張表就是因為當初字段設計不合理。這套房屋租賃系統(tǒng)里最核心的是四張表user用戶house房源housestate租賃訂單/狀態(tài)collect收藏。用戶表不必多說需要區(qū)分管理員和普通用戶我一般在role字段里用數(shù)字區(qū)分1表示管理員2表示房東3表示租客。有同學問為什么不建三張用戶表這屬于過度設計。除非業(yè)務差異大到字段完全不同否則一張表加角色字段就夠了。房源表是信息密度最大的表我列出比較常見的字段設計思路你可以直接抄作業(yè)字段名類型說明idint主鍵user_idint發(fā)布者 IDtitlevarchar(100)房源標題typevarchar(20)出租方式整租/合租addressvarchar(255)小區(qū)地址areadouble面積平方米pricedouble月租價格imagestext圖片 URL多張用逗號分隔statusint0 待審核 / 1 已上架 / 2 已出租 / 3 已下架create_timedatetime發(fā)布時間我這里特意把images字段設計成逗號分隔的字符串而不是單獨建一張圖片表。有人會爭論這樣做不規(guī)范但在這種小體量系統(tǒng)里這是一種性價比極高的方案查詢列表時只需查一張表減少一次子查詢或關聯(lián)查詢。如果你追求范式嚴格也可以拆圖片表但實際開發(fā)和畢設維護都會變麻煩沒必要。訂單表是整個業(yè)務核心它負責記錄一次租賃關系的完整狀態(tài)變化字段一般包括圖片、house_id、user_id、landlord_id、start_time、end_time、rent_month 等尤其是status字段要靈活動態(tài)化因為后續(xù)接口邏輯全靠它判斷。3.2 狀態(tài)字段與關鍵索引設計狀態(tài)字段是最容易被小看的。比如房源狀態(tài)我建議用int而不是字符串原因很簡單字符串一旦拼寫不一致比如 上架 和 上架 直接會導致列表查不到數(shù)據(jù)數(shù)字枚舉在代碼里寫常量或枚舉類可讀性一點不差。后續(xù)你寫 SQL比如 查詢所有已上架房源就是WHERE status 1清晰高效。訂單表的狀態(tài)流轉我習慣用狀態(tài)機思維設計避免業(yè)務邏輯到處亂寫。比如一次租賃訂單可以經(jīng)歷這些狀態(tài)0待確認1已確認等待支付/簽約2租賃中3已到期4已取消5已退租這樣一來每次狀態(tài)變化的判定都集中到service層的一個方法里不會出現(xiàn) 在 controller 里偷偷改字段 的爛代碼。索引設計也不能只靠默認主鍵。在這個項目里有兩個查詢場景頻率最高用戶查看自己的發(fā)布記錄user_id查詢、平臺端按狀態(tài)篩選列表status查詢。所以我會在user_id和status上各自加一個普通索引。數(shù)據(jù)量不大時可能看不出差別但這是好習慣也方便你在答辯時說清楚 為什么加索引。4. 后端接口與核心邏輯實現(xiàn)4.1 登錄認證與權限控制登錄認證是后端最重要的一塊。這個項目常用 JWT 或者 Session我建議優(yōu)先使用 JWT因為它是無狀態(tài)的Android 端拿到 Token 存本地即可服務端升級擴容、橫向擴展時沒有 Session 同步問題。我簡單描述一下 JWT 的接入思路。用戶調用/user/login提交用戶名和密碼后端校驗通過后用 secret key 生成 Token返回給 Android。Token 里可以帶上 userId、role 這些關鍵信息但千萬不要把密碼放進去。之后每次 Android 發(fā)起需要身份認證的請求都在 header 里攜帶token: xxx。Spring Boot 通過一個攔截器或者過濾器在進入 controller 前解析 Token、校驗合法性和有效期再把用戶信息放到 request 里。寫攔截器時有幾個容易踩的坑不需要認證的路徑比如登錄、注冊、房源列表必須放行否則用戶沒登錄就看不到任何內容。Token 過期后要返回統(tǒng)一格式的錯誤信息Android 端收到后需要做 重新登錄 的跳轉提示而不是讓界面卡在空白頁面。管理員的接口要校驗 role 字段防止普通用戶通過直接調用接口刪房源。4.2 租賃流程中的關鍵接口與狀態(tài)機圍繞房屋租賃我認為最值得學習的是兩個接口添加房源和創(chuàng)建訂單。添加房源接口的流程我前面提過這里補充一個關鍵點圖片上傳接口一定不能和房源新增接口耦合成一個接口否則圖片較大時請求超時概率很高。正確做法是先后端提供一個/upload返回圖片 URL再把 URL 拼接進房源數(shù)據(jù)。這樣一方面提高了重試的靈活性另一方面方便復用。上傳建議限制圖片大小比如單張最大 5MB格式校驗白名單防止用戶傳非圖片文件進來。創(chuàng)建訂單接口的難點在于并發(fā)和防重復。業(yè)務中租客可能手快點了兩次 立即租房如果不做處理會出現(xiàn)同一房源同時生成兩筆訂單。處理方法不復雜在創(chuàng)建訂單前先查詢該房源當前狀態(tài)是否為 已上架如果不是就返回明確提示同時給用戶 ID 加一個防重復提交標記比如 Redis 里面存一個 key這是企業(yè)級做法。但如果你不想引入 Redis也可以在 SQL 層面做判斷用一個帶house_id status的唯一約束兜底讓數(shù)據(jù)庫去擋第二次提交。下面這段代碼可以幫你理解查詢房源列表時常用的條件組合寫法public IPageHouse getHouseList(int page, int size, String type, Integer status, String keyword) { LambdaQueryWrapperHouse 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這套交互協(xié)議在真實項目里也是標準做法。4.3 參數(shù)校驗與統(tǒng)一返回格式很多新手項目最大的問題是前后端各寫各的字段名對不上錯誤提示對不上。要避免這個問題從第一天就要定好統(tǒng)一返回結構。我常用這樣的 JSON 格式{ code: 0, message: success, data: {} }code0表示成功非 0 表示失敗。message是給用戶看的提示比如 用戶名或密碼錯誤、圖片大小不能超過5MB。Android 端解析的時候只用判斷code是否為 0再拿到data來做后續(xù)邏輯非常省事。參數(shù)校驗要依托Validated注解加實體類字段約束比如NotBlank、NotNull、Min。千萬別把一堆 if 判斷寫在 controller 里誰維護誰知道痛苦。當然校驗不通過的異常也要集中處理用RestControllerAdvice ExceptionHandler把異常統(tǒng)一包裝成上面的 JSON 格式再返回給 Android 端。這一塊雖然在界面上看不到但在后端代碼評審時加分極多。5. Android 端實現(xiàn)細節(jié)5.1 項目結構與網(wǎng)絡層搭建Android 端的源碼質量直接決定這個項目給人第一印象好不好。我最反感的是把幾百行代碼全部寫在 MainActivity 里頁面一多就變成 面條代碼。這個項目我建議至少分這么幾類包bean存放對應后端返回結構的實體類。api存放接口定義比如登錄接口、房源列表接口、收藏接口。adapter各個列表的適配器。activity/fragment頁面邏輯。utils公共工具類PrefUtil、網(wǎng)絡判斷等。網(wǎng)絡層不用過度封裝但至少要做一個單例。我講解視頻里最常演示的是用 OkHttp 封裝一個HttpUtil類內部維護一個 OkHttpClient 實例對外提供 get 和 post 方法。再在此基礎上加一個回調接口把成功和失敗分發(fā)出去這樣 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 會崩潰。這個細節(jié)很多初學者會忽略也是很常見的 Android 崩潰點。5.2 頁面清單與功能拆解在界面設計上一個完整租賃 App 至少要包含這些頁面登錄、注冊頁。主界面首頁房源列表或輪播推薦、類型篩選、個人中心用 BottomNavigationView Fragment 實現(xiàn)。房源詳情頁圖片輪播、房屋信息、房東信息、收藏按鈕、預約看房/立即租房按鈕。發(fā)布房源頁表單錄入 多圖選擇上傳。我的訂單頁區(qū)分 我發(fā)布的 和 我租賃的。后臺管理頁房源審核、用戶列表、數(shù)據(jù)概覽。房源列表頁通常用 RecyclerView 展示Adapter 里的 item 布局要包含標題、價格、戶型、面積、封面圖。這里有一個實際經(jīng)驗列表頁不要一次性把所有字段都加載進來尤其是images字段較長很容易拖慢首次加載速度。更合理的做法是在列表接口中只返回圖片的第一張也就是前端截取逗號分隔結果的第一個這樣列表加載會明顯變快。圖片加載直接上 Glide。Glide 的好處是支持占位圖、錯誤圖、內存和磁盤緩存還能自動處理 OOM 風險。如果你還在用它加載網(wǎng)絡圖片時忘記加路徑比如把http://192.168.1.100:8080/upload/1.png寫成本地路徑那一定加載不出來。5.3 登錄狀態(tài)與 Token 管理Android 端的登錄態(tài)保存最簡單可靠的是 SharedPreferences。登錄成功以后把 Token、userId、role 存下來在每次網(wǎng)絡請求的 header 里帶上 Token用戶在個人中心點擊退出登錄時清除這些數(shù)據(jù)并跳回登錄頁。這個流程看起來簡單實際有幾個細節(jié)要注意。第一SharedPreferences 提交要用apply()而不是commit()因為前者是異步寫入不會卡主線程。第二不能把所有頁面都要求登錄比如首頁房源列表一定要允許未登錄訪問否則搜索引擎和分享鏈接的場景會非常尷尬。第三當某個接口返回 Token 失效時Android 端最好能統(tǒng)一彈出一個對話框讓用戶重新登錄而不是在每個 Activity 里各自判斷??梢杂靡粋€全局的 Activity 管理工具在檢測到失效碼時關閉所有頁面并回到登錄頁這樣體驗才像一個真正的商業(yè) App。6. 運行部署與調試經(jīng)驗6.1 本地啟動步驟一個新環(huán)境要跑起這個系統(tǒng)順序很重要。先啟動 MySQL創(chuàng)建好數(shù)據(jù)庫house_rental修改后端配置里的數(shù)據(jù)庫連接字符串然后啟動 Spring Boot 項目。建議在src/main/resources下的application.yml中用外置配置覆蓋默認配置這樣換環(huán)境不用改源碼。你可以這樣配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/house_rental?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/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指向模擬器自己。用真機時填電腦的局域網(wǎng) IP并且保證手機和電腦連的是同一個 Wi-Fi。6.2 常見聯(lián)調問題與排查思路聯(lián)調階段我整理了下面幾個高頻問題幾乎每個初學者都會遇到現(xiàn)象原因解決辦法Android 請求接口超時后端服務沒啟動或地址填錯先看后端控制臺日志再確認 IP/端口中文亂碼數(shù)據(jù)庫字符集不是 utf8mb4在建庫語句里指定DEFAULT CHARSETutf8mb4同時連接串加characterEncodingutf8圖片上傳后加載 404后端靜態(tài)資源映射未配置在 WebMvcConfig 中配置/upload/**資源映射到本地磁盤路徑房源列表一直加載失敗JSON 字段名與 JavaBean 不對應打印后端返回 JSON檢查 Bean 字段名確認有無SerializedName真機訪問不了后端手機與電腦不在同一局域網(wǎng)/防火墻攔截關防火墻或放行 8080 端口用別的手機熱點測試關于跨域Android 原生 App 其實不受瀏覽器跨域限制但會有明文流量限制。如果后端是 http 而 Android 較高版本默認禁止明文你需要在AndroidManifest.xml里設置usesCleartextTraffictrue不然請求會直接被攔截。這個問題特別隱蔽我見過很多同學在模擬器上正常、在真機上掛了半天找不到原因。7. 二次開發(fā)建議與避坑清單7.1 新手最容易踩的坑這個項目如果要作為畢設或者真實項目交付有幾個坑我真心建議避開。第一個坑是時間字段的設計。很多新手會把starttime、endtime字段設計成 String直接存 2024-06-01 這樣的文本。這樣做的危害是排序、比較租期時會出錯而且無法使用 MySQL 的時間函數(shù)。規(guī)范做法是用datetime類型后端用LocalDateTime接收和返回前端再格式化展示。第二個坑是刪除數(shù)據(jù)的粗暴操作。房源、訂單這類數(shù)據(jù)千萬別用物理刪除。一旦用戶不小心誤刪或者面試官問 如何恢復數(shù)據(jù)你就難堪了。建議加一個is_deleted字段做邏輯刪除MyBatis-Plus 也支持配置TableLogic注解查詢時自動過濾刪除時自動改為 update非常省心。第三個坑是接口權限遺漏。很多畢設項目只在界面上做了按鈕隱藏但接口完全沒有權限過濾用戶只要抓個包直接調用接口就能刪別人房源。這個系統(tǒng)里至少要保證 修改、刪除、審核 三類接口都必須校驗管理員或資源擁有者身份。實現(xiàn)方式是在 service 里傳入當前用戶 ID再和資源的user_id比對不一致就直接拋出 無權操作。7.2 可擴展方向如果答辯時間充裕你完全可以在基礎功能上加幾個亮點成本和收益都高。在地圖上展示房源位置后端存經(jīng)緯度字段Android 端引入高德或者百度地圖 SDK做一個房源地圖視圖。這個功能視覺沖擊力強且實現(xiàn)不復雜。增加短信驗證碼登錄借助第三方短信服務傳遞用戶手機號和驗證碼替換一部分密碼登錄場景可以展示你對消息通信的理解。增加爬蟲或者數(shù)據(jù)字典比如在后臺統(tǒng)計各區(qū)域房源數(shù)、平均租金用 ECharts 或 Android 圖表框架展示。這是妥妥的加分項。租約支付如果要做得像商業(yè)產品還需要抽象支付回調流程但畢設階段不一定落地可以在文檔里描述清楚設計思路。另外提一句如果你想把這個項目包裝成簡歷項目切記不能只寫 實現(xiàn)了房屋租賃 CRUD。要突出你在權限控制、狀態(tài)機設計、防重復提交、圖片上傳優(yōu)化、真機聯(lián)調兼容性這些點上的思考這才是面試官想聽的東西。8. 實操體驗與收尾建議最后分享一點個人體會。帶過幾屆學生做類似的畢設和訓練營項目我發(fā)現(xiàn)大家最容易卡住的階段不是寫代碼而是 代碼跑起來之后不知道干什么。拿到這套系統(tǒng)源碼后我建議你按三步走第一步不看代碼把后端數(shù)據(jù)庫建出來用 Postman 測一遍主要接口感受一下接口的請求和響應結構第二步對照源碼把接口實現(xiàn)捋清楚畫一張自己看得懂的調用鏈路圖第三步改一個小功能比如給房源列表增加按價格排序的參數(shù)親自動手改代碼、重啟服務、驗證效果。走完這三步這套系統(tǒng)才真正變成了你的東西。如果你打算在畢設答辯里講這個項目多準備幾個 為什么 的回答。比如 為什么訂單狀態(tài)用 int 不用 String、為什么圖片地址用逗號拼接而不是關聯(lián)表、為什么登錄用 Token 不用 Session。這些問題都是加分機會也是真正體現(xiàn)你深入理解項目的時刻。做這套系統(tǒng)難度不在于堆功能而在于把每個模塊之間的邊界打磨清楚。服務端只提供接口邏輯客戶端只負責展示和交互數(shù)據(jù)庫用合理字段支撐業(yè)務流轉。這套思路放到任何業(yè)務系統(tǒng)里都適用。希望這篇分享能幫你少走一些彎路把項目做得比大多數(shù)模板更有底氣。