简介:这是一套面向Java全栈初学者与课程设计学生的汽车租赁系统实战源码,基于SpringBoot后端与Vue.js前端构建,完整覆盖用户管理、车辆调度、订单处理等核心业务场景,适合作为毕业设计、大作业或SpringBoot+Vue技术栈入门实践项目。资源共150个文件,包含57个Java业务逻辑与控制器类、26个JPG与25个PNG格式的界面截图及图标资源、19个XML配置文件(含MyBatis映射)、15个编译后的class文件,以及application.yml、logback-spring.xml等关键配置,整体压缩包仅1.18MB,轻量易部署。已有146人学习下载,代码经导师指导并获98分高分评价,所有模块均通过严格调试,可直接运行验证功能逻辑。读者可获得结构清晰的前后端分离工程、Shiro权限控制实现(含MyRealm、ShiroConfig等)、Druid连接池配置、AOP日志切面、以及涵盖Controller/Service/DAO/Entity/Tests的完整分层代码体系。
1. 为什么一个「汽车租赁系统」要死磕 SpringBoot + Vue 的前后端分离落地?
你手头刚接了个毕设或外包需求:做个汽车租赁系统,支持用户选车、下单、支付、还车、评价,管理员能管车辆、订单、司机、会员。老板/导师说“用 SpringBoot 和 Vue 做”,但没告诉你——真正卡住90%人的不是功能逻辑,而是前后端联调时的跨域404、Vue路由刷新白屏、SpringBoot静态资源404、JWT token在axios里漏传、Vue打包后路径错乱导致CSS/JS 404……这些不是bug,是架构缝合失败的创口。
这个标题里的.zip很关键:它不是空泛的“技术栈介绍”,而是一个可解压、可运行、可二次开发的最小可行工程包。它代表一种工业级落地范式——后端用 SpringBoot 做 RESTful API(含 MyBatis-Plus 操作 MySQL、Redis 缓存订单状态、MinIO 存车辆图片),前端用 Vue 3 + Pinia + Vue Router 实现单页应用(SPA),二者通过 Nginx 反向代理解耦部署。不是“理论上能跑”,而是你unzip后改两行配置就能mvn clean package+npm run build,本地跑通登录、选车、下单全流程。
适合三类人:
✅应届生:毕设答辩前一周还在改接口返回格式?这个结构能让你把精力聚焦在业务逻辑(比如租车超期计费规则、车辆可用性校验),而不是被No 'Access-Control-Allow-Origin' header报错拖垮;
✅转岗前端:Vue 路由怎么配才能让/car/detail/123正确加载?Pinia 怎么持久化用户登录态?这里给你真实项目里的router/index.ts和stores/user.ts;
✅中小公司后端:不想再维护 JSP 或 Thymeleaf 模板?SpringBoot 打成 jar 包丢到服务器,Vue 打包成静态文件扔进 Nginx,运维成本直降70%——本篇就拆解这个压缩包里每个文件的真实作用和必调参数。
别被“租赁系统”四个字骗了——它本质是一套可复用的 SaaS 基础骨架:用户中心、权限控制(RBAC)、文件上传(MinIO)、定时任务(Quartz 清理过期订单)、日志审计(AOP)。你替换成“设备租赁”“图书借阅”“会议室预约”,改3个实体类+5个SQL语句就能复用。现在,我们从解压开始,一砖一瓦重建这个系统。
2. 解压即运行:SpringBoot 后端启动的 4 个硬核检查点
拿到基于SpringBoot+Vue的汽车租赁系统.zip,先别急着npm install。90% 的“跑不起来”问题,根子在后端启动阶段就被埋下了。我习惯按以下顺序逐项验证,比盲目查日志快3倍:
2.1 检查 JDK 版本与 SpringBoot 兼容性(血泪经验:别信 pom.xml 里写的 version)
打开pom.xml,找到<parent>标签下的spring-boot-starter-parent版本号。常见组合如下(注意:SpringBoot 3.x 要求 JDK 17+,而多数老项目仍是 2.7.x):
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 这是当前最稳定的 LTS 版本 --> <relativePath/> </parent>提示:如果你本地 JDK 是 8 或 11,强行运行 SpringBoot 3.x 会直接报
java.lang.IllegalStateException: Unable to load the config class。用java -version确认后,要么升级 JDK,要么降级 SpringBoot 版本(修改<version>并删掉target/目录重编译)。
2.2 数据库连接配置:application.yml 里这 3 行决定能否连上 MySQL
进入src/main/resources/application.yml,重点看spring.datasource配置段。不要只改url,必须同步校验 driver-class-name 和 username/password:
spring: datasource: url: jdbc:mysql://localhost:3306/car_rental?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 注意:MySQL 8+ 必须用 cj 版本驱动!- ✅
url中serverTimezone=Asia/Shanghai是刚需:否则时间字段插入时区错乱,订单创建时间变成 1970; - ✅
driver-class-name必须匹配 MySQL 版本:MySQL 5.7 用com.mysql.jdbc.Driver,8.0+ 必须用com.mysql.cj.jdbc.Driver; - ✅
username/password别照抄root/123456—— 本地 MySQL 如果改过密码,这里必须同步更新,否则启动时抛Access denied for user。
2.3 MyBatis-Plus 自动建表:启动前手动执行 SQL 初始化数据库
SpringBoot 默认不会自动建库建表(除非你开了spring.sql.init.mode=always,但生产环境严禁这么干)。这个压缩包通常附带sql/car_rental.sql文件,内容类似:
CREATE DATABASE IF NOT EXISTS car_rental CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE car_rental; CREATE TABLE `car_info` ( `id` bigint NOT NULL AUTO_INCREMENT, `brand` varchar(50) NOT NULL COMMENT '品牌', `model` varchar(100) NOT NULL COMMENT '车型', `license_plate` varchar(20) NOT NULL COMMENT '车牌号', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:1-可用,0-维修中', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;操作步骤:
- 在 MySQL 客户端(如 Navicat 或命令行)新建数据库
car_rental;- 执行
car_rental.sql全部语句;- 启动 SpringBoot 时观察控制台日志:若看到
Mapped Statements列出CarInfoMapper.selectList等方法,说明 MyBatis-Plus 已成功扫描到 Mapper 接口。
2.4 Redis 与 MinIO 配置:两个“可选但强烈建议启用”的服务
虽然系统能无 Redis/MinIO 启动,但车辆图片上传、订单缓存、登录态校验会直接失效。检查application.yml中这两段:
# Redis 配置(用于存储 JWT token 和热点数据) spring: redis: host: localhost port: 6379 password: database: 0 # MinIO 配置(用于存储车辆图片、合同PDF) minio: endpoint: http://localhost:9000 access-key: minioadmin secret-key: minioadmin bucket: car-images- ✅ 启动 MinIO:下载 MinIO Server 后执行
minio server ./data,浏览器访问http://localhost:9000,用minioadmin/minioadmin登录,手动创建car-imagesBucket; - ✅ Redis:若未安装,用 Docker 一行启动:
docker run -d --name redis -p 6379:6379 -d redis:7-alpine; - ✅关键验证:启动 SpringBoot 后,访问
http://localhost:8080/api/v1/car/list,若返回 JSON 数据且含carImages字段(如"imageUrl": "http://localhost:9000/car-images/abc123.jpg"),说明 MinIO 集成成功。
3. Vue 前端启动:从npm install到首页渲染的 5 个致命细节
后端跑通只是第一步。Vue 项目常因环境差异导致npm run serve卡在Compiling...或白屏。我们按真实调试路径拆解:
3.1 Node.js 版本锁定:Vue 3 要求 ≥16.13.0,但别用最新版
执行node -v,确认版本 ≥16.13.0。强烈建议用 nvm 管理多版本(避免全局污染):
# macOS/Linux curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 16.20.2 nvm use 16.20.2为什么不用 18.x 或 20.x?
Vue CLI 4.x(本项目大概率使用)对 Node 18+ 支持不稳定,npm install时可能报ERR_OSSL_PEM_ROUTINE;而 Node 16.20.2 是长期支持(LTS)中最稳的版本,兼容所有 Vue 3 生态。
3.2vue.config.js:解决跨域、静态资源路径、打包输出的三大开关
这是 Vue 项目里唯一必须修改的配置文件。打开根目录下的vue.config.js,重点关注:
const path = require('path') module.exports = { // 1. 开发环境跨域代理(解决 /api/* 请求被浏览器拦截) devServer: { proxy: { '/api': { target: 'http://localhost:8080', // SpringBoot 后端地址 changeOrigin: true, pathRewrite: { '^/api': '' // 把请求路径中的 /api 去掉,转发给后端 } } } }, // 2. 静态资源路径(解决打包后 CSS/JS 404) publicPath: process.env.NODE_ENV === 'production' ? './' : '/', // 3. 输出目录(确保与 Nginx 配置一致) outputDir: 'dist' }- ✅
devServer.proxy:Vue 开发服务器(localhost:8081)收到/api/v1/car/list请求,自动转发到http://localhost:8080/v1/car/list,避免在代码里写死http://localhost:8080; - ✅
publicPath: './':Vue CLI 3+ 默认publicPath为'/',但打包后放到 Nginx 的子目录(如http://example.com/rental/)时,CSS/JS 会请求http://example.com/css/app.xxx.css而非http://example.com/rental/css/app.xxx.css,加'./'强制相对路径; - ✅
outputDir: 'dist':和 SpringBoot 的resources/static目录对应(见第4章)。
3.3 Axios 请求拦截器:JWT token 自动注入的正确姿势
打开src/utils/request.js,这是整个前端调用后端 API 的统一入口。核心逻辑是:
import axios from 'axios' // 创建 axios 实例 const request = axios.create({ baseURL: '/api', // 与 vue.config.js 中的 proxy 保持一致 timeout: 10000 }) // 请求拦截器:自动添加 token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` // 注意 Bearer 后有空格! } return config }, error => Promise.reject(error)) // 响应拦截器:统一错误处理 request.interceptors.response.use(response => response.data, error => { if (error.response?.status === 401) { localStorage.removeItem('token') router.push('/login') // 跳转登录页 } return Promise.reject(error) }) export default request避坑点:
config.headers.Authorization = 'Bearer ' + token中的空格不能少,否则 SpringBoot 的JwtAuthenticationFilter会解析失败;response.data是关键:SpringBoot 返回的是{code: 200, data: {...}, msg: 'success'},前端直接取response.data就能拿到业务数据,无需response.data.data;401处理必须清 token 并跳转,否则用户刷新页面后仍处于“已登录”假象。
3.4 Vue Router 路由守卫:登录态校验与动态路由加载
src/router/index.ts是 SPA 的导航中枢。重点看beforeEach全局守卫:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next('/login') // 需要登录的页面且无 token,跳转登录 } else if (to.path === '/login' && token) { next('/dashboard') // 已登录却访问登录页,跳转首页 } else { next() // 放行 } })- ✅
to.meta.requiresAuth:在路由定义中显式声明,例如:{ path: '/car/list', component: () => import('@/views/CarList.vue'), meta: { requiresAuth: true } } - ✅动态路由加载:车辆详情页
/car/detail/:id使用props: true,在组件内直接通过props.id获取参数,而非this.$route.params.id(Vue 3 Composition API 写法); - ✅路由懒加载:所有
component: () => import(...)写法,确保首屏加载速度(Webpack 自动代码分割)。
3.5 Pinia 状态管理:用户信息持久化与响应式更新
src/stores/user.ts是登录态的核心:
import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || '', userInfo: JSON.parse(localStorage.getItem('userInfo') || '{}') }), actions: { setToken(token: string) { this.token = token localStorage.setItem('token', token) // 持久化到 localStorage }, setUserInfo(userInfo: any) { this.userInfo = userInfo localStorage.setItem('userInfo', JSON.stringify(userInfo)) }, clear() { this.token = '' this.userInfo = {} localStorage.removeItem('token') localStorage.removeItem('userInfo') } } })为什么用 Pinia 不用 Vuex?
Vue 3 官方推荐 Pinia,API 更简洁(无 mutations/types/constants),且天然支持 TypeScript。localStorage持久化是刚需——否则页面刷新后用户登出;clear()方法在退出登录时必须调用,否则 token 仍在本地残留。
4. 前后端联调:Nginx 反向代理 + 静态资源部署的黄金配置
开发阶段用vue.config.js代理能跑通,但上线必须用 Nginx 统一入口。否则会出现:Vue 路由刷新 404、SpringBoot 静态资源无法访问、HTTPS 证书无法统一管理。以下是生产环境标准配置:
4.1 Nginx 配置文件:一份搞定反向代理与静态资源托管
编辑/etc/nginx/conf.d/car-rental.conf(Linux)或C:\nginx\conf\car-rental.conf(Windows):
upstream backend { server 127.0.0.1:8080; # SpringBoot 应用 } server { listen 80; server_name localhost; # 1. Vue 前端静态资源(dist 目录) location / { root /var/www/car-rental/dist; # Linux 路径,Windows 改为 C:/nginx/html/dist index index.html; try_files $uri $uri/ /index.html; # 关键!解决 Vue Router 刷新 404 } # 2. 后端 API 接口代理(/api/* 转发到 SpringBoot) location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 3. MinIO 文件直链(/minio/* 直接访问 MinIO) location /minio/ { proxy_pass http://localhost:9000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }关键参数解释:
try_files $uri $uri/ /index.html:当请求/car/detail/123时,Nginx 先找dist/car/detail/123文件(不存在)→ 再找dist/car/detail/123/目录(不存在)→ 最后返回dist/index.html,由 Vue Router 解析路由;proxy_pass http://backend/:末尾的/表示去除路径前缀,/api/v1/car/list→http://127.0.0.1:8080/v1/car/list;location /minio/:车辆图片 URL 如http://your-domain.com/minio/car-images/abc.jpg,直接穿透到 MinIO,不经过 SpringBoot,减轻后端压力。
4.2 SpringBoot 静态资源路径:resources/static与dist的映射关系
SpringBoot 默认将src/main/resources/static目录作为静态资源根路径。但Vue 打包后的dist目录需复制到这里,才能让 SpringBoot 直接托管(备用方案,不推荐用于生产):
# 构建 Vue 项目 cd frontend npm run build # 复制 dist 到 SpringBoot 的 static 目录(仅开发测试用) cp -r dist/* ../backend/src/main/resources/static/注意:此方式下,访问
http://localhost:8080/即显示 Vue 首页,但存在严重缺陷:
- Vue Router 刷新仍 404(SpringBoot 的
WebMvcConfigurer未配置addViewControllers);- 无法利用 Nginx 的 gzip 压缩、HTTP/2、负载均衡等能力;
- 生产环境必须用 Nginx 分离部署,SpringBoot 只暴露 API 端口(8080),不暴露静态资源。
4.3 HTTPS 配置:Let's Encrypt 免费证书一键部署
生产环境必须启用 HTTPS。用 Certbot 自动获取证书:
# Ubuntu 安装 certbot sudo apt update sudo apt install certbot python3-certbot-nginx # 获取证书(替换 your-domain.com) sudo certbot --nginx -d your-domain.com # 自动续期(Certbot 已配置 cron) sudo certbot renew --dry-run更新 Nginx 配置,启用 SSL:
server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # 其余 location 配置同上... } # HTTP 强制跳转 HTTPS server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; }安全提醒:SpringBoot 的
application.yml中,server.ssl.key-store等配置必须删除,HTTPS 由 Nginx 终止,后端只需 HTTP 通信,降低 TLS 计算开销。
5. 避坑指南:SpringBoot + Vue 联调的 5 个高频翻车现场
注意:以下问题均来自真实项目踩坑记录,按发生频率排序。每条包含现象、原因、解决方案,拒绝“重启试试”。
5.1 现象:Vue 页面空白,控制台报Failed to resolve component: router-view
原因:Vue Router 版本与 Vue 3 不兼容。压缩包里package.json的"vue-router": "^4.0.0"是正确版本,但若你执行npm install vue-router未指定版本,可能装入^3.0.0(Vue 2 专用)。
解决:
- 删除
node_modules和package-lock.json; - 执行
npm install vue-router@4; - 检查
src/router/index.ts是否使用createRouter而非new Router()。
5.2 现象:SpringBoot 启动报Caused by: java.lang.ClassNotFoundException: org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
原因:SpringBoot 版本与 Spring Cloud 版本不匹配。例如spring-cloud-dependencies:Hoxton.SR12要求 SpringBoot 2.3.x,而项目用 2.7.18。
解决:
- 查
pom.xml中<spring-cloud.version>标签; - 访问 Spring Boot & Spring Cloud 版本兼容表 ;
- 将
<spring-cloud.version>改为2021.0.8(对应 SpringBoot 2.7.x); - 执行
mvn clean compile。
5.3 现象:车辆图片上传后,MinIO 控制台能看到文件,但前端 URL 返回http://localhost:9000/car-images/xxx.jpg无法访问
原因:MinIO 默认只允许localhost访问,且未配置 CORS(跨域资源共享)。浏览器因同源策略阻止加载。
解决:
- 启动 MinIO 时添加
--console-address :9001参数(开放控制台); - 执行以下命令配置 CORS(替换
your-domain.com为你的域名):mc admin config set myminio service web-cors="allow-origin=https://your-domain.com,allow-methods=GET,PUT,POST,DELETE,allow-headers=*" mc admin config save myminio mc admin service restart myminio
5.4 现象:登录成功后,localStorage有 token,但后续请求 Header 无Authorization
原因:Axios 拦截器未生效。常见于main.ts中未正确引入request.js,或request.interceptors.request.use被包裹在条件判断中。
解决:
- 检查
main.ts是否有import '@/utils/request'; - 在
request.js的interceptors.request.use回调函数第一行加console.log('Request intercepted'); - 打开浏览器开发者工具,查看 Console 是否输出该日志;
- 若无输出,说明拦截器未注册,检查
request.js是否被其他模块覆盖。
5.5 现象:Nginx 配置正确,但访问http://your-domain.com/api/v1/car/list返回 502 Bad Gateway
原因:SpringBoot 未监听127.0.0.1:8080,而是绑定了0.0.0.0:8080(正常),但更可能是防火墙阻止了 8080 端口。
解决:
- 在服务器执行
curl http://127.0.0.1:8080/api/v1/car/list,确认 SpringBoot 自身可访问; - 若返回数据,则检查防火墙:
sudo ufw status(Ubuntu)或sudo firewall-cmd --list-ports(CentOS); - 开放端口:
sudo ufw allow 8080或sudo firewall-cmd --permanent --add-port=8080/tcp; - 重启防火墙:
sudo ufw reload或sudo firewall-cmd --reload。
6. 进阶技巧:用 Swagger + Postman 快速验证 API,再用 Jest 做前端单元测试
光跑通不算结束。真正的工程化交付,需要可验证、可回归、可协作的闭环。下面这两个技巧,让我在团队交接时少解释3小时:
6.1 SpringBoot 集成 Swagger:自动生成 API 文档并在线调试
在pom.xml中添加依赖:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>创建配置类SwaggerConfig.java:
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.example.carrental.controller")) .paths(PathSelectors.any()) .build() .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("汽车租赁系统 API 文档") .description("提供车辆、订单、用户相关接口") .version("1.0") .build(); } }启动后访问http://localhost:8080/swagger-ui.html,所有 Controller 方法自动生成文档,点击Try it out即可发送请求,无需 Postman 手动填参数。特别适合给测试同学快速上手。
6.2 Vue 组件单元测试:用 Jest 验证 CarList.vue 的列表渲染逻辑
在src/views/__tests__/CarList.spec.ts中写测试:
import { mount } from '@vue/test-utils' import CarList from '@/views/CarList.vue' import { createPinia } from 'pinia' describe('CarList.vue', () => { it('renders car list correctly', async () => { const pinia = createPinia() const wrapper = mount(CarList, { global: { plugins: [pinia] } }) // 模拟 API 返回数据 const mockCars = [ { id: 1, brand: 'Toyota', model: 'Camry', license_plate: '粤B12345' } ] wrapper.vm.cars = mockCars // 直接赋值 data await wrapper.vm.$nextTick() expect(wrapper.findAll('.car-item').length).toBe(1) expect(wrapper.find('.car-brand').text()).toBe('Toyota') }) })运行测试:npm run test:unit。这不是形式主义——当产品经理突然要求“车辆列表加个搜索框”,你改完代码后,npm test通过即证明原有逻辑未破坏。
6.3 生产环境部署 checklist:5 个必须确认的项
| 检查项 | 检查方法 | 不通过后果 |
|---|---|---|
| SpringBoot 日志级别为 INFO | application.yml中logging.level.root: INFO | DEBUG 级别日志刷爆磁盘 |
| Vue 打包开启 gzip | vue.config.js中configureWebpack: { plugins: [new CompressionPlugin()] } | 首屏加载慢 2~3 秒 |
| Nginx 开启 gzip 压缩 | nginx.conf中gzip on; gzip_types text/plain application/json; | CSS/JS 体积大 3 倍 |
| MinIO 持久化挂载 | docker run -v /data/minio:/data minio/minio server /data | 重启容器后图片丢失 |
| SpringBoot Actuator 健康检查 | 访问http://localhost:8080/actuator/health返回{"status":"UP"} | 运维无法监控服务状态 |
最后说句实在话:这个.zip项目的价值,不在于它实现了多少功能,而在于它把 SpringBoot + Vue 的协作边界划得足够清晰——后端只管数据和业务规则,前端只管交互和体验,中间用 RESTful API 和 Nginx 当“翻译官”。你以后做任何系统,只要把car_info表换成book_info,把rent_order换成borrow_order,改 3 个实体类、5 个 SQL、2 个 Vue 页面,就能复用整套架构。这才是工程师该追求的复用,不是 Ctrl+C/V。
希望帮到你。
本文还有配套的精品资源,点击获取