☰
SpringBoot+Vue宠物领养系统全栈实战:从建库到上线六天搞定
2026/10/8 11:11:20 网站建设 项目流程

简介:这份资源面向计算机相关专业的毕业生与课程设计开发者,提供一套基于Spring Boot与Vue的宠物领养系统完整实现方案,可用于毕业设计、论文写作或全栈项目练手。系统功能覆盖宠物列表与详情、领养公告、个人中心等前台模块,普通用户可管理我的申请、陪伴记录与宠物知识,管理员则负责种类与品种管理、宠物信息维护、领养审核、宠物回访及公告发布,业务闭环较为完整。资源包共4个文件,包含2个zip源码压缩包、1个sql数据库脚本和1个docx部署文档,整体约664KB,源码、建库脚本与部署说明齐备,便于快速还原运行环境。文档中还配有系统架构图、用例图、顺序图与E-R图等专业绘图,可直接支撑论文中的需求分析与设计章节。目前已有42人学习,适合需要完整赛题方案、数据库结构与部署思路的读者参考借鉴。

1. 宠物领养系统从零到上线:一套 SpringBoot+Vue 全栈方案到底能跑多快

宠物领养这件事,线下靠微信群和朋友圈转发,信息散、状态乱、领养人和送养人之间来回问,一只猫从发布到被领走平均要折腾三四轮沟通。我去年帮本地一家流浪动物救助站做了一套基于 SpringBoot+Vue 的宠物领养系统,从建库到部署上线一共用了六天,核心诉求就三个:宠物信息能结构化录入、领养申请能走审批流、后台能看数据。这套东西适合谁?适合想拿一个完整全栈项目练手的后端初学者,也适合小型救助站或宠物店想自己搭一套内部管理工具的技术负责人。技术栈选型上,SpringBoot 做后端接口和业务逻辑,Vue 做前后端分离的前台展示和管理后台,MySQL 存数据,部署用 Nginx 加 Jar 包的方式。整套方案不依赖任何云服务,一台 2 核 4G 的轻量服务器就能跑起来。下面我把从环境搭建到部署上线的完整路径拆开讲,包括数据库表怎么设计、接口怎么分层、Vue 路由怎么配、打包后怎么塞进 SpringBoot 里一起部署,以及我踩过的那些坑。

2. 环境搭建与项目骨架:SpringBoot 和 Vue 各自怎么初始化

2.1 后端骨架:用 Spring Initializr 生成可运行的最小工程

我一般不会手动去建 Maven 目录结构,直接用 Spring Initializr 生成骨架最省事。访问 start.spring.io,选 Maven 项目、Java 17、SpringBoot 3.2.x(注意别选太新的版本,后面说为什么),依赖勾选 Spring Web、MyBatis Framework、MySQL Driver、Lombok。生成后解压,用 IDEA 打开,目录结构长这样:

pet-adoption/ ├── src/main/java/com/example/petadoption/ │ ├── PetAdoptionApplication.java │ ├── controller/ │ ├── service/ │ ├── mapper/ │ ├── entity/ │ └── config/ ├── src/main/resources/ │ ├── application.yml │ └── mapper/ └── pom.xml

pom.xml 里关键依赖确认一下:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

逻辑说明:spring-boot-starter-web 提供内嵌 Tomcat 和 MVC 能力,mybatis-spring-boot-starter 负责 ORM 映射,mysql-connector-j 是 JDBC 驱动。参数上,MyBatis 的版本要跟 SpringBoot 3.x 对齐,用 3.0.3 以上,否则会出现 SqlSessionFactory 找不到的启动报错。

application.yml 的最小配置:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/pet_adoption?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.petadoption.entity

这里有个参数容易被忽略:serverTimezone 必须显式指定,否则 MySQL 8.x 会报时区错误。数据库名 pet_adoption 要提前建好,字符集用 utf8mb4,不然宠物名字里的特殊字符会乱码。

2.2 前端骨架:Vue 3 项目初始化与路由配置

前端我用 Vue 3 加 Vite 的组合,比 Webpack 快很多。初始化命令:

npm create vite@latest pet-adoption-web -- --template vue cd pet-adoption-web npm install npm install vue-router@4 axios element-plus

安装完依赖后,在 src 下建 router/index.js:

import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', component: () => import('../views/Home.vue') }, { path: '/pets', component: () => import('../views/PetList.vue') }, { path: '/pet/:id', component: () => import('../views/PetDetail.vue') }, { path: '/apply/:petId', component: () => import('../views/ApplyForm.vue') }, { path: '/admin', component: () => import('../views/Admin.vue'), children: [ { path: 'pets', component: () => import('../views/admin/PetManage.vue') }, { path: 'applications', component: () => import('../views/admin/ApplyManage.vue') } ] } ] const router = createRouter({ history: createWebHistory(), routes }) export default router

逻辑说明:createWebHistory 用的是 HTML5 History 模式,URL 干净但需要 Nginx 配 try_files 回退,否则刷新页面会 404。路由懒加载用 () => import() 写法,打包时会自动分包。参数上,vue-router 4 是 Vue 3 专用版本,别装成 3.x。axios 用来调后端接口,element-plus 提供表格和表单组件,管理后台基本靠它撑起来。

2.3 前后端联调:跨域配置和接口代理

开发阶段前端跑在 5173 端口,后端跑在 8080,直接调接口会跨域。两种解法:后端加 CORS 配置,或者前端 Vite 配代理。我一般两个都配,开发用代理,生产用 Nginx 转发。

后端 CORS 配置类:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }

前端 vite.config.js 代理:

export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

逻辑说明:allowedOriginPatterns 用 * 而不是 allowedOrigins,是因为 allowCredentials 为 true 时后者不允许通配符。changeOrigin 设为 true 让代理请求的 Host 头跟目标一致。开发时前端请求 /api/pets 会被代理到 localhost:8080/api/pets,不用改代码。

3. 数据库设计与核心表结构:宠物、领养申请、用户三张主表怎么定

3.1 表结构设计:从业务字段反推 DDL

宠物领养系统的核心业务就三条线:宠物信息管理、领养申请审批、用户角色区分。我设计了五张表,主表三张,辅助两张。

CREATE TABLE `user` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL, `password` VARCHAR(100) NOT NULL, `role` TINYINT DEFAULT 0 COMMENT '0普通用户 1管理员', `phone` VARCHAR(20), `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `pet` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL, `species` VARCHAR(20) COMMENT '猫/狗/其他', `breed` VARCHAR(50), `age` INT, `gender` TINYINT COMMENT '0公 1母', `health_status` VARCHAR(100), `description` TEXT, `image_url` VARCHAR(255), `status` TINYINT DEFAULT 0 COMMENT '0待领养 1已申请 2已领养', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `adoption_apply` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `pet_id` BIGINT NOT NULL, `user_id` BIGINT NOT NULL, `reason` TEXT, `living_condition` VARCHAR(200), `status` TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2拒绝', `apply_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `audit_time` DATETIME, PRIMARY KEY (`id`), KEY `idx_pet_id` (`pet_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

逻辑说明:user 表的 role 字段用 TINYINT 区分权限,比字符串省空间且索引快。pet 表的 status 字段是核心状态机,0 到 2 的流转控制领养流程。adoption_apply 表用 pet_id 和 user_id 做外键关联,加索引是因为后台按宠物查申请列表是高频操作。字符集统一 utf8mb4,宠物描述里可能有 emoji。

3.2 MyBatis 映射:XML 写 SQL 还是注解

简单查询用注解,复杂动态查询用 XML。宠物列表带多条件筛选,我写在 XML 里:

<select id="selectByCondition" resultType="Pet"> SELECT * FROM pet <where> <if test="species != null and species != ''"> AND species = #{species} </if> <if test="status != null"> AND status = #{status} </if> <if test="keyword != null and keyword != ''"> AND (name LIKE CONCAT('%', #{keyword}, '%') OR breed LIKE CONCAT('%', #{keyword}, '%')) </if> </where> ORDER BY create_time DESC </select>

逻辑说明:where 标签自动处理第一个 AND,if 标签按条件拼接。参数用 #{} 是预编译占位符,防 SQL 注入,别用 ${}。keyword 模糊查询用 CONCAT 拼接通配符,MySQL 和 PostgreSQL 都兼容。

对应的 Mapper 接口:

@Mapper public interface PetMapper { List<Pet> selectByCondition(@Param("species") String species, @Param("status") Integer status, @Param("keyword") String keyword); int insert(Pet pet); int updateStatus(@Param("id") Long id, @Param("status") Integer status); }

@Param 注解让 XML 里能直接用参数名引用,不加的话只能按 param1、param2 引用,容易搞混。

3.3 状态流转与事务控制

领养申请审批通过时,要同时更新申请状态和宠物状态,必须在一个事务里:

@Service public class AdoptionService { @Autowired private AdoptionApplyMapper applyMapper; @Autowired private PetMapper petMapper; @Transactional(rollbackFor = Exception.class) public void approveApply(Long applyId, Long petId) { applyMapper.updateStatus(applyId, 1); petMapper.updateStatus(petId, 2); } }

逻辑说明:@Transactional 的 rollbackFor 设为 Exception.class,默认只回滚 RuntimeException,受检异常不会触发回滚。两步更新要么都成功要么都失败,否则会出现申请通过了但宠物还是待领养状态的脏数据。参数上,隔离级别用默认的 REPEATABLE READ 就够,这种低频写场景不用调。

4. 前后端功能实现:领养申请流程和后台管理的接口与页面

4.1 领养申请接口:从提交到审批的完整链路

领养申请是这套系统的核心业务流。用户在前端填表提交,后端接收后落库,管理员在后台审批。Controller 层:

@RestController @RequestMapping("/api/apply") public class ApplyController { @Autowired private AdoptionService adoptionService; @PostMapping("/submit") public Result submit(@RequestBody ApplyDTO dto) { // 校验宠物是否已被领养 Pet pet = adoptionService.getPetById(dto.getPetId()); if (pet.getStatus() == 2) { return Result.fail("该宠物已被领养"); } adoptionService.submitApply(dto); return Result.success(); } @PostMapping("/audit") public Result audit(@RequestBody AuditDTO dto) { adoptionService.approveApply(dto.getApplyId(), dto.getPetId()); return Result.success(); } }

逻辑说明:submit 接口先查宠物状态,已领养的直接拒绝,避免重复申请。Result 是统一返回体,包含 code、msg、data 三个字段。参数上,ApplyDTO 里带 petId、userId、reason、livingCondition,前端表单字段跟 DTO 一一对应。

Service 层的提交逻辑:

public void submitApply(ApplyDTO dto) { AdoptionApply apply = new AdoptionApply(); BeanUtils.copyProperties(dto, apply); apply.setStatus(0); apply.setApplyTime(new Date()); applyMapper.insert(apply); // 更新宠物状态为已申请 petMapper.updateStatus(dto.getPetId(), 1); }

BeanUtils.copyProperties 做 DTO 到 Entity 的字段拷贝,省去手动 set。注意属性名要一致,否则拷不过去。

4.2 前端页面:宠物列表和申请表单的 Vue 实现

宠物列表页用 element-plus 的卡片布局:

<template> <div class="pet-list"> <el-row :gutter="20"> <el-col :span="6" v-for="pet in pets" :key="pet.id"> <el-card :body-style="{ padding: '0px' }"> <img :src="pet.imageUrl" class="pet-image" /> <div class="pet-info"> <h3>{{ pet.name }}</h3> <p>{{ pet.species }} · {{ pet.breed }} · {{ pet.age }}个月</p> <el-tag :type="pet.status === 0 ? 'success' : 'info'"> {{ pet.status === 0 ? '待领养' : '已领养' }} </el-tag> <el-button @click="$router.push(`/pet/${pet.id}`)">查看详情</el-button> </div> </el-card> </el-col> </el-row> </div> </template> <script setup> import { ref, onMounted } from 'vue' import axios from 'axios' const pets = ref([]) onMounted(async () => { const res = await axios.get('/api/pet/list', { params: { status: 0 } }) pets.value = res.data.data }) </script>

逻辑说明:script setup 是 Vue 3 的组合式 API 写法,ref 定义响应式数据,onMounted 在组件挂载后调接口。axios 的 params 会自动拼成查询字符串。el-tag 的 type 根据状态动态切换颜色。

申请表单页:

<template> <el-form :model="form" label-width="100px"> <el-form-item label="申请理由"> <el-input v-model="form.reason" type="textarea" :rows="4" /> </el-form-item> <el-form-item label="居住条件"> <el-input v-model="form.livingCondition" /> </el-form-item> <el-form-item> <el-button type="primary" @click="submit">提交申请</el-button> </el-form-item> </el-form> </template> <script setup> import { reactive } from 'vue' import { useRoute, useRouter } from 'vue-router' import axios from 'axios' import { ElMessage } from 'element-plus' const route = useRoute() const router = useRouter() const form = reactive({ petId: route.params.petId, reason: '', livingCondition: '' }) const submit = async () => { if (!form.reason) { ElMessage.warning('请填写申请理由') return } await axios.post('/api/apply/submit', { ...form, userId: 1 }) ElMessage.success('申请已提交') router.push('/pets') } </script>

逻辑说明:reactive 定义表单对象,route.params.petId 从路由参数取宠物 ID。提交前做非空校验,成功后跳转列表页。userId 这里写死为 1,实际项目要从登录态取。

4.3 后台管理:审批列表和状态操作

后台审批页用 el-table 展示申请列表,带通过和拒绝按钮:

<template> <el-table :data="applications" style="width: 100%"> <el-table-column prop="id" label="申请ID" width="80" /> <el-table-column prop="petName" label="宠物名称" /> <el-table-column prop="username" label="申请人" /> <el-table-column prop="reason" label="申请理由" show-overflow-tooltip /> <el-table-column label="状态" width="100"> <template #default="{ row }"> <el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag> </template> </el-table-column> <el-table-column label="操作" width="180"> <template #default="{ row }"> <el-button v-if="row.status === 0" size="small" type="success" @click="audit(row, 1)">通过</el-button> <el-button v-if="row.status === 0" size="small" type="danger" @click="audit(row, 2)">拒绝</el-button> </template> </el-table-column> </el-table> </template>

逻辑说明:el-table-column 的 template #default 插槽拿到当前行数据 row,根据 status 渲染不同按钮。show-overflow-tooltip 让长文本溢出时显示省略号加悬浮提示。audit 方法调后端接口后刷新列表。

后端审批接口要联表查宠物名和用户名,SQL 这样写:

SELECT a.*, p.name AS petName, u.username FROM adoption_apply a LEFT JOIN pet p ON a.pet_id = p.id LEFT JOIN user u ON a.user_id = u.id ORDER BY a.apply_time DESC

LEFT JOIN 保证即使宠物被删了申请记录也能查出来,实际项目里宠物一般做逻辑删除,不会物理删。

5. 部署上线:Vue 打包塞进 SpringBoot 还是 Nginx 分开部署

5.1 两种部署方案对比与选择

部署这块有两条路:一是 Vue 打包后把 dist 目录塞进 SpringBoot 的 static 目录,打成一个 Jar 包;二是前端 dist 放 Nginx,后端 Jar 单独跑,Nginx 反代 API 请求。我两种都试过,说下区别。

对比项合并部署(Jar 包)分离部署(Nginx)
部署复杂度低,一个 Jar 搞定中,要配 Nginx
前端更新要重新打包 Jar替换 dist 目录即可
性能静态资源走 TomcatNginx 处理静态资源更快
跨域无需 Nginx 反代
适用场景小型项目、演示正式环境、前后端独立迭代

我一般正式环境用分离部署,演示或内部工具用合并部署。下面两种都讲。

5.2 合并部署:Vue 打包产物放进 SpringBoot static 目录

Vue 项目打包:

npm run build

生成的 dist 目录里有 index.html 和 assets 文件夹。把这两个东西复制到 SpringBoot 的 src/main/resources/static/ 下。然后改一下 vue-router 的模式,如果用 createWebHistory 需要后端配一个 fallback:

@Controller public class WebConfig { @RequestMapping(value = "/{path:[^\\.]*}") public String forward() { return "forward:/index.html"; } }

逻辑说明:这个映射把所有不带点的路径都转发到 index.html,让 Vue Router 接管路由。带点的路径(如 .js、.css)不匹配,正常走静态资源。参数上,正则 [^\.]* 表示不含点的任意字符。

然后打包 SpringBoot:

mvn clean package -DskipTests java -jar target/pet-adoption-0.0.1-SNAPSHOT.jar

访问 localhost:8080 就能看到前端页面,API 请求也走同一个端口,没有跨域问题。

5.3 分离部署:Nginx 配置与后端 Jar 守护

前端 dist 上传到服务器 /var/www/pet-adoption/ 目录,Nginx 配置:

server { listen 80; server_name your_domain.com; location / { root /var/www/pet-adoption; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

逻辑说明:try_files 依次尝试文件、目录,都找不到就回退到 index.html,解决 History 模式刷新 404。proxy_pass 把 /api/ 开头的请求转发到后端 8080 端口。proxy_set_header 传递真实 Host 和客户端 IP,后端记日志时能拿到真实来源。

后端 Jar 用 systemd 守护:

[Unit] Description=Pet Adoption System After=network.target [Service] User=www ExecStart=/usr/bin/java -jar /opt/pet-adoption/app.jar Restart=always RestartSec=10 [Install] WantedBy=multi-user.target

逻辑说明:Restart=always 让进程崩溃后自动拉起,RestartSec=10 表示 10 秒后重启。User=www 用非 root 用户跑,安全一些。配置放到 /etc/systemd/system/pet-adoption.service,然后 systemctl enable 和 start。

5.4 数据库初始化与备份

上线前把建表 SQL 在服务器 MySQL 里执行一遍。备份用 mysqldump:

mysqldump -u root -p pet_adoption > /backup/pet_adoption_$(date +%Y%m%d).sql

逻辑说明:$(date +%Y%m%d) 生成日期后缀,每天备份一个文件。可以加到 crontab 里每天凌晨跑一次。恢复用 mysql -u root -p pet_adoption < backup.sql。

提示:生产环境数据库密码别写死在 application.yml 里,用环境变量或者外部配置文件覆盖。

6. 避坑与排查:这套系统上线前后最容易翻车的五个地方

6.1 坑一:SpringBoot 版本太高导致 MyBatis 不兼容

现象:启动报错 Invalid value type for attribute 'factoryBeanObjectType',或者 SqlSessionFactory 创建失败。

原因:SpringBoot 3.2 以上对 MyBatis 的 FactoryBean 做了改动,旧版 mybatis-spring-boot-starter 不兼容。

解决:要么把 SpringBoot 降到 3.1.x,要么把 mybatis-spring-boot-starter 升到 3.0.3 以上。我一般选后者,新项目直接用配套的新版本。

6.2 坑二:Vue 打包后刷新页面 404

现象:开发环境正常,部署后点导航没问题,但按 F5 刷新就白屏或 404。

原因:createWebHistory 模式下,/pets 这种路径在服务器上找不到对应的物理文件。

解决:Nginx 加 try_files $uri $uri/ /index.html,或者合并部署时加那个 forward 映射。用 createWebHashHistory 也能绕开,但 URL 带 # 不好看。

6.3 坑三:MySQL 时区导致时间差 8 小时

现象:数据库里存的时间和实际时间差 8 小时,或者启动报 The server time zone value is unrecognized。

原因:JDBC 连接没指定时区,MySQL 用了系统默认时区。

解决:连接 URL 加 serverTimezone=Asia/Shanghai,或者 MySQL 配置文件里设 default-time-zone='+08:00'。两个都配最稳。

6.4 坑四:图片上传路径在打包后失效

现象:开发时图片能上传能显示,打成 Jar 包后上传报错或图片访问 404。

原因:代码里用了相对路径或者 src/main/resources 下的路径,打包后这些路径不存在。

解决:上传目录配成绝对路径,比如 /opt/pet-adoption/upload/,然后加一个静态资源映射:

@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:/opt/pet-adoption/upload/"); } }

逻辑说明:addResourceLocations 用 file: 前缀指向文件系统绝对路径,不要用 classpath。这样上传的图片存在 Jar 包外面,重新部署不会丢。

6.5 坑五:并发申请同一只宠物导致状态覆盖

现象:两个人同时申请同一只宠物,两条申请都提交成功,宠物状态被改成已申请,但管理员审批时不知道该批哪个。

原因:submit 接口先查状态再更新,查和更新之间有时间窗口,并发时两个请求都查到待领养状态。

解决:在 updateStatus 的 SQL 里加状态条件:

UPDATE pet SET status = 1 WHERE id = #{id} AND status = 0

然后判断 affected rows,如果返回 0 说明状态已被改过,回滚事务并提示用户。这是乐观锁的思路,不用加锁就能解决大部分并发场景。

7. 进阶技巧:用 SpringBoot 自定义自动配置把通用能力抽出来

这套系统做完之后,我又接了第二个类似的项目,发现用户认证、统一返回体、异常处理这些代码每个项目都要抄一遍。后来我把它们抽成了一个 starter,新项目引一个依赖就搞定。这个技巧对做过两三个 SpringBoot 项目的人特别有用。

先建一个独立的 Maven 模块,pom 里引 spring-boot-autoconfigure:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency>

然后写自动配置类:

@AutoConfiguration @ConditionalOnWebApplication public class CommonAutoConfiguration { @Bean @ConditionalOnMissingBean public GlobalExceptionHandler globalExceptionHandler() { return new GlobalExceptionHandler(); } @Bean @ConditionalOnMissingBean public ResultAdvice resultAdvice() { return new ResultAdvice(); } }

逻辑说明:@AutoConfiguration 是 SpringBoot 2.7 之后的新注解,替代原来的 @Configuration 加 spring.factories。@ConditionalOnMissingBean 保证用户自己定义了同类型 Bean 时以用户的为准,不覆盖。@ConditionalOnWebApplication 限定只在 Web 环境生效。

然后在 src/main/resources/META-INF/spring/ 下建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,内容写一行类的全限定名:

com.example.common.CommonAutoConfiguration

逻辑说明:这个文件是 SpringBoot 3.x 的自动配置注册方式,2.7 之前用的是 spring.factories。文件名和路径不能错,否则配置不生效。

验证方法:在业务项目里引这个 starter 的依赖,启动后看日志有没有加载 CommonAutoConfiguration,或者故意抛个异常看 GlobalExceptionHandler 有没有生效。如果没生效,检查 imports 文件路径和类名是否一致,这是最常见的翻车点。

参数上,如果你想让某些配置可开关,加 @ConditionalOnProperty:

@Bean @ConditionalOnProperty(name = "common.exception.enabled", havingValue = "true", matchIfMissing = true) public GlobalExceptionHandler globalExceptionHandler() { return new GlobalExceptionHandler(); }

matchIfMissing = true 表示配置项不写时默认开启,写了 false 才关闭。这样业务项目可以在 application.yml 里灵活控制。

我现在的习惯是每做完一个项目,就把里面跟业务无关的代码往 starter 里挪一点,攒到第三个项目的时候基本就是引依赖加改配置的事了。这套宠物领养系统里的统一返回体、全局异常处理、分页工具类,后来都进了我的 common-starter。你要是也在做类似的全栈项目,不妨从第一个项目就开始抽,别等到复制粘贴到吐了才想起来。希望帮到你。

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

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

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

立即咨询