SpringBoot租房系统实战:Maven构建、JPA查询与JWT认证
2026/9/14 14:29:00 网站建设 项目流程

简介:这是一份基于Spring Boot的租房管理系统完整工程,主要面向Java后端初学者、毕业设计及课程设计人群,同时适用于需要快速搭建前后端分离项目的开发者。系统围绕用户管理、房源发布、多条件搜索、预约看房、在线签约交易以及后台权限管理等业务展开,涵盖从数据库表设计、后端接口开发到前端页面联调的完整链路,可作为综合性实战参考。资源包共827个文件,约16.58MB,包含146个Java后端源码、52个Vue前端组件、50个HTML页面、153个JavaScript脚本、44个CSS样式表,并带有SQL初始化脚本、项目启动与构建脚本及若干备份配置,结构清晰,便于导入开发环境直接运行和二次开发。已有48人学习下载。包内提供一键安装、启动、打包脚本,同时保留了部分页面和配置的备份,方便对照修改与排错;整体采用Spring Boot主流技术栈,覆盖数据持久化、安全控制与前端交互,适合作为课设、毕设或实习项目的完整参考。

1. 一套带 .bak 备份的 SpringBoot 租房系统,先看它替我们踩过哪些坑

接手这样一个压缩包:目录里同时躺着index.html.bakupdate-password.vue.bakIndexAsideStatic.vue.bakBreadCrumbs.vue.bakIndexHeader.vue.bak,又躺着3-build.bat2-run.bat1-install.batmvnw.cmd——这说明它不是一个简单的毕设源码,而是一个经历过“用 IDE 启动 → 改成命令行批处理 → 前端页面被改坏后留了备份”的真实开发产物。基于 SpringBoot 的租房管理系统,解决的是房东发布房源、租客按位置价格筛选房源、在线预约看房、合同与支付状态管理这一整条业务链路。适合拿来学习 SpringBoot + Vue 前后端分离项目如何落地者、需要快速交付中小型管理系统的开发人员,也适合准备 SpringBoot 相关面试时找真实场景做复盘的人。前端保留.bak文件这一点,反而比只有一个干净 README 的源码包更有信息量——它告诉你哪里被改过、改之前长什么样。

2. SpringBoot 工程骨架与 Maven Wrapper 构建脚本拆解

2.1 mvnw.cmd 和 .classpath 在团队协作中的真实作用

.classpath是 Eclipse 项目的类路径文件,说明最初有人用 Eclipse 开发过这个租房系统;mvnw.cmd是 Maven Wrapper 的 Windows 批处理版本,它不依赖机器上是否安装了 Maven。第一次接触这个资源的人,最容易犯的错误是直接双击2-run.bat,发现报错再去装 Maven——实际上mvnw.cmd会自动下载指定版本的 Maven 到本地仓库,保证所有人构建环境一致。

# 查看 mvnw.cmd 指向的 Maven 版本(在项目根目录执行) mvnw.cmd -v

提示:Maven Wrapper 的核心是项目里的.mvn/wrapper/maven-wrapper.properties,里面写死了distributionUrl。建议打开确认 Maven 版本是否与你本机 JDK 兼容,SpringBoot 2.x 用 JDK 8/11,SpringBoot 3.x 必须 JDK 17 以上,版本错配是启动报错第一来源。

2.2 三个 bat 脚本的分工与执行顺序

资历够的开发者会把安装依赖、启动应用、打包构建拆成三个独立脚本,避免一条命令做太多事导致排错困难。这个包里的三个 bat 便是这种思路,执行顺序是1-install.bat2-run.bat,最后才用3-build.bat出产物。下面脚本内容是按 SpringBoot 项目常规用法还原的,实际以 rar 内文件为准。

@echo off REM 1-install.bat:清理并安装依赖到本地仓库,跳过测试 call mvnw.cmd clean install -DskipTests pause @echo off REM 2-run.bat:以开发模式启动 SpringBoot 应用 call mvnw.cmd spring-boot:run pause @echo off REM 3-build.bat:打包成可执行 jar,同样跳过测试以加快速度 call mvnw.cmd clean package -DskipTests pause

参数说明:clean删除target目录,防止旧 class 干扰;-DskipTests只跳过测试执行但会编译测试代码,-Dmaven.test.skip=true连测试代码都不编译,后者更适合没有测试类的小型管理项目。第一次拿到资源,先执行1-install.bat让依赖进入本地仓库,再执行2-run.bat看控制台是否打印出 SpringBoot 启动横幅,确认无误后再用3-build.bat打 jar 包。

2.3 application.yml 中最关键的 SpringBoot 配置项

租房管理系统的后端配置集中在src/main/resources/application.yml中。SpringBoot 自动装配在这里体现得最明显:引入spring-boot-starter-data-jpa后,只要在配置里填好数据源,它就会自动创建DataSourceEntityManagerFactory和事务管理器,不用手写 XML。以下是一份典型配置:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/rent_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true open-in-view: false jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

各参数的作用:ddl-auto: update让 Hibernate 按实体类自动建表或加列,开发期省事,但生产环境要改成validate或直接用 Flyway 管理表结构;show-sql: true打印实际执行的 SQL,排查 MyBatis 或 JPA 的 N+1 问题时非常有用;open-in-view: false关闭视图层会话保持,避免懒加载导致的事务范围失控;jackson.date-format解决后端返回LocalDateTime时前端拿到序列化数字的问题。如果 rar 里用的是 MyBatis-Plus,则把spring.datasource保留,删掉spring.jpa,换成mybatis-plus.mapper-locations: classpath*:mapper/**/*.xmlmybatis-plus.configuration.map-underscore-to-camel-case: true。验收时先看启动日志中的端口占用情况,8080 被占用就加--server.port=8081临时覆盖,不需要改文件重打包。

3. 租房领域建模与持久化检索实操

3.1 房源、用户、预约三张核心表的结构设计

这个系统涉及的信息很多,但落到数据库只需先抓住三张主表:用户表、房源表、预约看房表。交易合同与支付状态可以挂在房源表上以状态字段推进,不必一开始就拆出五张表。以下是一种开发期可跑通的设计:

表名关键字段字段类型说明
rent_userid / username / password / phone / roleBIGINT / VARCHAR / VARCHAR / VARCHAR / TINYINTrole 0=租客 1=房东
house_infoid / user_id / title / location / price / house_type / statusBIGINT / BIGINT / VARCHAR / VARCHAR / DECIMAL / VARCHAR / TINYINTstatus 0=已下架 1=已上架
appointmentid / house_id / user_id / visit_time / note / statusBIGINT / BIGINT / BIGINT / DATETIME / VARCHAR / TINYINTstatus 0=待确认 1=已确认

house_info.location建议存结构化文本而不使用 MySQL Point 类型,原因是对接前端搜索框时按字符串LIKE匹配最直接,且不需要额外引入 GIS 依赖。价格用DECIMAL(10,2)而不是FLOAT,避免租金金额出现精度漂移——这是租房业务里比较容易触发的问题:按月租金 5000.6 元,用 float 存储后再计算押一付三,可能会出现 15001.8 变成 15001.79 的尴尬。

3.2 使用 Spring Data JPA 编写条件查询与分页

持久层在 SpringBoot 里最常见的实现是JpaRepository加方法名派生查询,只要接口方法名符合规范,Spring 会自动生成实现类,不需要手写 SQL。房源的搜索筛选就是典型场景:租客按位置、价格区间、房屋类型组合查询,同时要分页。代码如下:

package com.example.rent.repository; import com.example.rent.entity.HouseInfo; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; public interface HouseRepository extends JpaRepository<HouseInfo, Long> { @Query("SELECT h FROM HouseInfo h WHERE h.status = 1 " + "AND (:location IS NULL OR h.location LIKE CONCAT('%', :location, '%')) " + "AND (:minPrice IS NULL OR h.price >= :minPrice) " + "AND (:maxPrice IS NULL OR h.price <= :maxPrice) " + "AND (:houseType IS NULL OR h.houseType = :houseType)") Page<HouseInfo> search(@Param("location") String location, @Param("minPrice") java.math.BigDecimal minPrice, @Param("maxPrice") java.math.BigDecimal maxPrice, @Param("houseType") String houseType, Pageable pageable); }

逻辑说明:@Query里每个条件都写了:xxx IS NULL的兜底判断,前端不传该参数时传 null,SQL 会跳过这个过滤条件。LIKE CONCAT('%', :location, '%')避免用户直接拼接%造成恶意搜索条件,也兼容了模糊匹配。Pageable由 Spring 从请求参数里自动解析,底层自动生成LIMITCOUNT语句,比手写PageHelper的 ThreadLocal 方式更符合 JPA 规范,也少一类“分页插件误拦截其他 SQL”的问题。

调用时只需在 Controller 里接收前端参数,构造PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "price"))传给接口。注意 Spring Data 的页码从 0 开始,而前端表格插件通常从 1 开始,这里要统一约定:接口文档写明page=0表示第一页。因此需要将前端传的页码做减一处理,处理完后要写清楚转换依据,避免同事接手时把页码越调越偏。

3.3 Controller 层封装统一返回体与异常处理

搜索接口的完整代码需要配合统一返回类和异常处理才能在生产环境里用。这个系统的 Controller 层一般会返回一个Result<T>结构:codemessagedata。下面是租房搜索接口的写法:

@RestController @RequestMapping("/api/house") public class HouseController { private final HouseRepository houseRepository; public HouseController(HouseRepository houseRepository) { this.houseRepository = houseRepository; } @GetMapping("/search") public Result<Page<HouseInfo>> search( @RequestParam(required = false) String location, @RequestParam(required = false) BigDecimal minPrice, @RequestParam(required = false) BigDecimal maxPrice, @RequestParam(required = false) String houseType, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "price")); return Result.ok(houseRepository.search(location, minPrice, maxPrice, houseType, pageable)); } }

参数说明:required = false表示搜索条件允许为空;defaultValue = "0"给 page 一个安全默认值,比抛异常更友好;排序放在PageRequest里,保证分页和排序在同一个查询中生效。价格区间查询的前端要约定好规则,比如最小价格和最大价格都为空时表示不限。为了查询不走全表扫描,后续优化可以在location字段上加普通索引,并用EXPLAIN SELECT ... WHERE location LIKE 'xxxx%'验证是否命中索引。城市字段如果会频繁过滤,则比模糊搜索更适合做等值查询。

4. Spring Security 认证与前端 Vue 页面的登录联动

4.1 登录接口如何签发 Token 并保护租房接口

租房管理系统的用户模块涉及注册、登录、密码找回,后端不能裸奔开放接口。常见做法是 Spring Security 加 JWT,无状态认证适合前后端分离的 Vue 项目。核心依赖是spring-boot-starter-securityio.jsonwebtoken:jjwt。安全配置类需要重写SecurityFilterChain,放行登录、注册和房源查询接口,其他接口统一校验Authorization头。放行列表是开发期比较容易踩坑的地方:漏放OPTIONS请求会导致前端跨域预检失败。

@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/login", "/api/auth/register", "/api/house/search").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .anyRequest().authenticated()) .exceptionHandling(ex -> ex.authenticationEntryPoint(restAuthenticationEntryPoint())); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }

逻辑说明:STATELESS表示后端不创建 Session,每次请求都靠 JWT 解析用户身份;addFilterBefore把自定义的JwtAuthenticationFilter放在 Spring Security 默认认证过滤器之前,让 JWT 先于账号密码认证执行;cors配置要允许前端开发服务器的地址,比如http://localhost:5173。这套设计不依赖 Redis 存储会话,在单机部署和集群部署中都能横向扩展,适合目前 100 万条以内房源数据的体量。

4.2 从 update-password.vue.bak 看密码修改链路的实现

这个 rar 里出现了update-password.vue.bak,说明它原本的前端已经实现了修改密码功能,但因为某种改动被备份。密码修改在后端最简单可靠的设计是:用户输入旧密码、新密码,后端校验旧密码是否正确,再对新密码加密存储。Spring Security 的BCryptPasswordEncoder每次生成的密文都不同,只能用matches方法校验,不能把密文取出来做字符串相等比较。接口代码如下:

@PutMapping("/api/user/password") public Result<Void> updatePassword(@RequestBody PasswordUpdateRequest request, Authentication authentication) { String username = authentication.getName(); RentUser user = userRepository.findByUsername(username); if (!passwordEncoder.matches(request.getOldPassword(), user.getPassword())) { return Result.error(4001, "旧密码不正确"); } if (!request.getNewPassword().equals(request.getConfirmPassword())) { return Result.error(4002, "两次输入的新密码不一致"); } user.setPassword(passwordEncoder.encode(request.getNewPassword())); userRepository.save(user); return Result.ok(); }

逻辑说明:这里没有手动校验新密码强度,生产环境应补充至少 8 位且包含字母和数字的正则校验;authentication.getName()从当前 JWT 上下文取用户名,不需要前端额外传递 userId,避免越权改别人密码。前端那块update-password.vue.bak备份里的表单,大概率是改了校验规则后产生了 bug 才留的备份,查看.bak文件能看到改动前用正则校验密码的版本,能实际帮助我们判断是“业务调整”还是“适配新接口”。

4.3 前端 axios 拦截器与角色权限的粗略对应

配套这套后端接口,前端需要在 axios 请求拦截器里把 token 放进请求头。下面的代码在IndexHeader.vue或全局请求封装文件里都能看到类似写法:

import axios from 'axios' const service = axios.create({ baseURL: '/api', timeout: 15000 }) // 请求拦截器:自动附带 JWT service.interceptors.request.use(config => { const token = localStorage.getItem('rent_token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }, error => { return Promise.reject(error) }) // 响应拦截器:401 时清除 token 并跳转登录页 service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('rent_token') window.location.href = '/login' } return Promise.reject(error) } ) export default service

参数说明:baseURL: '/api'配合前端开发服务器的 proxy 转发,把/api前缀的请求转发到后端 8080 端口,跨域问题在前端层面消除;超时时间 15 秒比较适合包含图片上传的房源发布接口,普通列表接口可以拆出更短超时的实例。IndexAsideStatic.vue是后端管理系统里左侧菜单栏的静态版本,菜单里“房源管理”“预约管理”“用户管理”这几个入口要实现权限控制,前端只做菜单显隐远远不够,真正拦截要在后端的@PreAuthorize("hasRole('ADMIN')")注解或 Security 配置的requestMatchers里控制。

5. .bak 文件对比排错与离线部署的兜底技巧

5.1 用 diff 快速找出前端改动点

既然压缩包自带多个.bak文件,最直接的用法是在项目目录里执行对比,观察当前版本和备份版本到底差在哪里:

diff frontend/src/views/system/user/update-password.vue base/update-password.vue.bak

Windows 环境没有diff命令时,用fc命令效果相同:

fc /N update-password.vue update-password.vue.bak

对比输出重点看三处:<template>里表单校验规则是否被删改、<script>里调用接口的 URL 是否从/api/user/updatePwd改成/api/user/passwordmessage弹窗组件是否更换。若差异集中在导入语句,说明改动了 Element UI 的组件引用方式;若差异集中在data中表单字段名,则说明后端实体字段发生过重命名。

5.2 前端构建产物由 SpringBoot 托管的部署顺序

部署时选择直接复制后端 jar 和前端 dist 目录分开部署也可以,但小团队更省事的做法是把前端构建产物交给 SpringBoot 托管,只启动一个 Java 进程。操作顺序是:前端npm run build生成dist目录,把dist里文件拷贝到src/main/resources/static/,再执行3-build.bat重新打包 jar。启动后用同一端口访问,SpringBoot 会优先从 classpath 的static目录找静态资源,找不到再路由到后端接口。

java -jar target/rent-system-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod

启动参数说明:--spring.profiles.active=prod加载application-prod.yml,里面应把ddl-auto改为validate,关闭show-sql,并设置management.endpoints.web.exposure.include=health,info做端点最小暴露。如果项目引用了 Actuator 的heapdump端点,默认在内存溢出时可以导出堆转储文件用于排查内存泄漏,但也有被下载的风险,因此最小暴露的原则是把不需要的端点关闭。此时访问首页会加载 Vue 构建后的index.html,后端接口通过/api前缀访问。

5.3 验证接口与静态资源 404 的最终检查

启动完成后不要急着打开浏览器,先用 curl 验证后端接口和数据是否正常:

curl -X GET "http://localhost:8080/api/house/search?location=&minPrice=1000&maxPrice=5000&page=0&size=5" -H "Authorization: Bearer YOUR_TOKEN"

能拿到 JSON 数据说明数据库连接与查询正常。然后访问首页http://localhost:8080/,如果页面能打开但接口请求失败,优先检查登录后 token 是否写入 localStorage,以及 axios 的baseURL是否为/api。如果出现静态资源 404,多半是dist拷贝到了src/main/resources而不是src/main/resources/static,检查 jar 包内的路径可以通过jar tf target/xxx.jar | grep index.html确认。后端日志只需重点看一条:启动时是否出现Tomcat started on port(s): 8080。看一眼有没有这行,就能判断系统到底卡在哪个环节,无需反复重启看花屏日志。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询