ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot租房系统实战:Maven构建、JPA查询与JWT认证

SpringBoot租房系统实战:Maven构建、JPA查询与JWT认证 简介这是一份基于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.bak、update-password.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak又躺着3-build.bat、2-run.bat、1-install.bat和mvnw.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/11SpringBoot 3.x 必须 JDK 17 以上版本错配是启动报错第一来源。2.2 三个 bat 脚本的分工与执行顺序资历够的开发者会把安装依赖、启动应用、打包构建拆成三个独立脚本避免一条命令做太多事导致排错困难。这个包里的三个 bat 便是这种思路执行顺序是1-install.bat到2-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.skiptrue连测试代码都不编译后者更适合没有测试类的小型管理项目。第一次拿到资源先执行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后只要在配置里填好数据源它就会自动创建DataSource、EntityManagerFactory和事务管理器不用手写 XML。以下是一份典型配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/rent_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/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: GMT8各参数的作用ddl-auto: update让 Hibernate 按实体类自动建表或加列开发期省事但生产环境要改成validate或直接用 Flyway 管理表结构show-sql: true打印实际执行的 SQL排查 MyBatis 或 JPA 的 N1 问题时非常有用open-in-view: false关闭视图层会话保持避免懒加载导致的事务范围失控jackson.date-format解决后端返回LocalDateTime时前端拿到序列化数字的问题。如果 rar 里用的是 MyBatis-Plus则把spring.datasource保留删掉spring.jpa换成mybatis-plus.mapper-locations: classpath*:mapper/**/*.xml和mybatis-plus.configuration.map-underscore-to-camel-case: true。验收时先看启动日志中的端口占用情况8080 被占用就加--server.port8081临时覆盖不需要改文件重打包。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 JpaRepositoryHouseInfo, 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)) PageHouseInfo 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的兜底判断前端不传该参数时传 nullSQL 会跳过这个过滤条件。LIKE CONCAT(%, :location, %)避免用户直接拼接%造成恶意搜索条件也兼容了模糊匹配。Pageable由 Spring 从请求参数里自动解析底层自动生成LIMIT和COUNT语句比手写PageHelper的 ThreadLocal 方式更符合 JPA 规范也少一类“分页插件误拦截其他 SQL”的问题。调用时只需在 Controller 里接收前端参数构造PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, price))传给接口。注意 Spring Data 的页码从 0 开始而前端表格插件通常从 1 开始这里要统一约定接口文档写明page0表示第一页。因此需要将前端传的页码做减一处理处理完后要写清楚转换依据避免同事接手时把页码越调越偏。3.3 Controller 层封装统一返回体与异常处理搜索接口的完整代码需要配合统一返回类和异常处理才能在生产环境里用。这个系统的 Controller 层一般会返回一个ResultT结构code、message、data。下面是租房搜索接口的写法RestController RequestMapping(/api/house) public class HouseController { private final HouseRepository houseRepository; public HouseController(HouseRepository houseRepository) { this.houseRepository houseRepository; } GetMapping(/search) public ResultPageHouseInfo 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-security和io.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 ResultVoid 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.bakWindows 环境没有diff命令时用fc命令效果相同fc /N update-password.vue update-password.vue.bak对比输出重点看三处template里表单校验规则是否被删改、script里调用接口的 URL 是否从/api/user/updatePwd改成/api/user/password、message弹窗组件是否更换。若差异集中在导入语句说明改动了 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.activeprod启动参数说明--spring.profiles.activeprod加载application-prod.yml里面应把ddl-auto改为validate关闭show-sql并设置management.endpoints.web.exposure.includehealth,info做端点最小暴露。如果项目引用了 Actuator 的heapdump端点默认在内存溢出时可以导出堆转储文件用于排查内存泄漏但也有被下载的风险因此最小暴露的原则是把不需要的端点关闭。此时访问首页会加载 Vue 构建后的index.html后端接口通过/api前缀访问。5.3 验证接口与静态资源 404 的最终检查启动完成后不要急着打开浏览器先用 curl 验证后端接口和数据是否正常curl -X GET http://localhost:8080/api/house/search?locationminPrice1000maxPrice5000page0size5 -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。看一眼有没有这行就能判断系统到底卡在哪个环节无需反复重启看花屏日志。本文还有配套的精品资源点击获取
返回列表