1. 从“能跑起来”到“跑成自己的”:二次开发的真实起点
很多人拿到一套微服务脚手架之后,第一反应是赶紧把服务启动起来,看到注册中心和网关都绿了,就觉得大功告成。但真正做过二次开发的人都知道,“能跑起来”和“跑成自己的项目”之间,隔着一条巨大的鸿沟。前者只是环境验证,后者才涉及架构理解、模块裁剪、命名重构、依赖梳理、配置体系重建这一整套动作。我见过太多项目,启动成功之后就开始改业务代码,结果改到一半发现认证逻辑跟自己的用户体系对不上,权限模型跟业务角色冲突,最后不得不推倒重来。
这篇文章要聊的,就是怎么把一套成熟的微服务后端脚手架,从零开始改造成真正属于自己业务的项目。整个过程不是简单的“改个包名、换个数据库”,而是需要你先理解它的分层逻辑、服务拆分思路、公共模块的职责边界,然后再做有策略的裁剪和替换。适合已经有 Java 和 Spring 基础、准备用微服务架构落地自己业务的中高级开发者,也适合那些接手了别人搭好的脚手架、需要快速理清改造路径的工程师。
我会按照真实的改造顺序来讲:先做架构摸底,再做工程重构,然后是认证与权限的替换,接着是配置体系和数据层的调整,最后聊服务裁剪和部署验证。每一步都会说明“为什么这么做”以及“我踩过哪些坑”,尽量让你少走弯路。
2. 动手之前先摸清骨架:模块划分与依赖关系梳理
2.1 先画一张自己的模块依赖图,别急着改代码
拿到脚手架的第一件事,绝对不是打开 IDE 改代码,而是先把它的模块结构彻底搞清楚。这类微服务项目通常会有十几个甚至二十几个 Maven 模块,包括网关、认证中心、若干业务服务、公共基础模块、代码生成模块、监控模块等等。你需要做的是:打开根pom.xml,把所有<modules>列出来,然后逐个看每个模块的pom.xml,搞清楚谁依赖谁。
我自己的做法是拿一张纸,画一棵依赖树。根节点是父工程,第一层是公共模块和业务模块,第二层是每个业务模块依赖了哪些公共模块。画完之后你会很直观地看到:哪些模块是“基础设施”(比如公共核心、公共安全、公共数据源),哪些是“业务服务”,哪些是“工具类模块”(比如代码生成、定时任务)。这张图决定了你后面裁剪的顺序——永远从叶子节点开始裁,不要先动根节点。
这里有个容易忽略的点:很多脚手架会把“公共安全”模块和“认证中心”模块耦合得很紧。公共安全模块里往往放了 JWT 工具类、用户上下文持有器、权限注解切面这些东西,而认证中心负责签发令牌。如果你打算换掉认证方案,这两个模块必须一起动,不能只改一个。
2.2 区分“框架能力”和“业务示例”,别把示例当框架
这是二次开发中最关键的判断力。脚手架里通常会有一些业务服务,比如“系统管理服务”“文件服务”“消息服务”,这些服务里有一部分是框架级能力(比如用户、角色、菜单、字典),有一部分只是演示用的示例代码(比如某个 demo 控制器)。你要做的是把“框架能力”保留并改造,把“业务示例”直接删掉或者替换成自己的业务。
怎么判断?我的经验是看这个模块被不被其他模块依赖。如果“系统管理服务”被网关的鉴权逻辑通过 Feign 调用了,那它就是框架能力,不能随便删。如果某个“测试服务”除了自己没有任何模块引用它,那它大概率就是示例,可以直接移除。另外看数据库表:用户表、角色表、权限表、字典表这些是框架级的,订单表、商品表这种就是示例业务表。
提示:在裁剪之前,先把整个项目完整启动一遍,确认所有服务都能注册成功、网关路由正常、认证流程通畅。这一步是为了建立一个“已知可用”的基线,后面改出问题了好对照排查。
2.3 版本对齐:别小看依赖版本这件事
这类脚手架通常会用一套统一的依赖版本管理,比如在父 pom 里通过<dependencyManagement>锁定 Spring Boot、Spring Cloud、MyBatis 等核心依赖的版本。你在改造时如果要引入新的依赖,一定要先看父 pom 里有没有已经管理了版本。如果自己贸然引入一个不同版本的同类依赖,很容易出现类冲突或者自动配置失效。
我遇到过一个典型问题:脚手架用的是某个版本的 Spring Security,我自己引入了一个更新版本的工具库,结果认证过滤器链的顺序全乱了,调试了大半天才发现是版本冲突。所以改造期间,所有新引入的依赖,优先在父 pom 的 dependencyManagement 里声明版本,子模块只写 groupId 和 artifactId。
3. 工程重构:包名、模块名与坐标体系的整体替换
3.1 包名替换不是全局搜索替换那么简单
很多人改包名就是 IDE 里全局替换com.oldname成com.newname,然后编译。这样做在纯 Java 代码层面没问题,但会漏掉几个地方:application.yml里的包扫描路径、MyBatis 的 mapper 扫描路径、日志配置里的 logger 名称、以及一些通过字符串反射加载类的配置。这些地方如果漏改,启动时不会报编译错误,但运行时会出问题,比如 mapper 扫描不到导致 Bean 注入失败。
我的做法是分三步:第一步,用 IDE 的重构功能改包名(不是文本替换),这样 Java 文件里的引用会自动更新;第二步,全局搜索旧包名的字符串,重点检查 yml、properties、xml 配置文件;第三步,启动所有服务,看日志里有没有“未找到 mapper”“类加载失败”这类错误。三步走完,包名替换才算干净。
3.2 模块重命名要同步改三处
模块重命名涉及三个地方:文件夹名、pom.xml里的<artifactId>、以及父 pom 里<modules>的声明。这三处必须同步改,漏一处就会导致 Maven 构建失败。另外,如果模块之间有依赖,被依赖模块的 artifactId 改了之后,依赖它的模块的pom.xml里的<dependency>也要跟着改。
这里有个小技巧:改模块名之前,先在根目录执行一次mvn clean install -DskipTests,确认当前状态能构建成功。改完之后再执行一次,对比两次的构建输出。如果第二次构建失败,错误信息会直接告诉你哪个模块的坐标对不上,定位起来很快。
3.3 统一项目坐标与版本号,为后续发布做准备
脚手架的 groupId 通常是它自己的组织标识,你要改成自己的。version 也建议从1.0.0-SNAPSHOT开始,而不是沿用脚手架原来的版本号。这样做的好处是,后面你发布到私有仓库或者做 CI/CD 时,版本管理清晰,不会跟原始脚手架混淆。
另外,如果脚手架用了<revision>属性或者 flatten 插件来统一管理版本,你要确认这些机制在改完坐标之后还能正常工作。我一般会在改完坐标后,执行一次mvn help:evaluate -Dexpression=project.version来验证版本号是否正确解析。
4. 认证与权限体系替换:从脚手架方案迁移到自己的用户模型
4.1 先搞清楚原认证流程的完整链路
在替换认证之前,你必须把原来的认证链路完整走一遍。通常的流程是:用户请求网关,网关的全局过滤器检查请求头里有没有令牌,没有就转发到认证中心;认证中心校验用户名密码,签发令牌;后续请求带着令牌,网关解析令牌并把用户信息放到请求头里传给下游服务;下游服务的拦截器从请求头里取出用户信息,构建用户上下文。
这条链路里,网关的过滤器和下游服务的拦截器是成对出现的,它们约定好了请求头的字段名和格式。你替换认证方案时,这两个地方必须一起改,否则会出现“网关认了但下游不认”或者反过来“下游认了但网关没传”的情况。
4.2 替换用户模型时,数据库表怎么处理
脚手架的用户表通常是围绕它自己的权限模型设计的,比如用户表、角色表、菜单表、用户角色关联表、角色菜单关联表。如果你自己的业务用户模型跟它差异很大,比如你需要多租户、需要用户分组、需要更细粒度的数据权限,那就不能直接复用这些表。
我的建议是:保留框架级的认证表结构,但扩展自己的业务用户表。具体做法是,认证中心仍然用原来的用户表做登录校验,但登录成功后返回的用户信息里,带上你自己业务用户表的主键。下游服务拿到这个主键后,去查自己的业务用户表获取详细信息。这样既不用大改认证逻辑,又能让业务层用自己的用户模型。
如果差异实在太大,那就只能替换认证中心的用户查询逻辑。这时候要注意,密码加密方式、令牌签发算法、令牌有效期这些都要同步调整,并且要确保网关和所有下游服务用的密钥是一致的。
4.3 权限注解与切面的适配
脚手架通常会用自定义注解加 AOP 切面的方式做权限校验,比如@RequiresPermissions("sys:user:add")这种。你替换权限模型后,这些注解里的权限标识要跟着改。更关键的是,切面里获取用户权限列表的逻辑要改——原来可能是从令牌里解析,或者从缓存里查,现在要改成从你的权限数据源获取。
这里有个性能上的注意点:权限校验是每个请求都要走的,如果每次校验都去查数据库,压力会很大。所以权限数据一定要走缓存,并且在权限变更时主动刷新缓存。我一般会在权限变更的接口里加一个缓存清除的逻辑,保证权限修改后能立即生效。
5. 配置体系与数据层改造:让项目跑在自己的环境里
5.1 配置中心的选择与迁移策略
这类脚手架通常支持多种配置方式,可能是本地配置文件,也可能是接入了配置中心。如果你打算用配置中心,建议在改造初期就把配置迁移过去,而不是等到后面再迁。因为配置中心涉及服务端搭建、客户端依赖引入、配置格式转换、动态刷新验证这一整套流程,越早做越容易发现问题。
迁移配置时,最容易出错的是配置的优先级。本地配置、配置中心配置、环境变量、启动参数,它们的优先级顺序要搞清楚。我遇到过配置中心里改了数据库连接但服务还是连旧库的情况,最后发现是本地application.yml里的配置优先级更高,把配置中心的覆盖了。
5.2 数据源与连接池的调整
脚手架默认的数据源配置通常是最小化的,你需要根据自己的业务量调整连接池参数。核心参数包括:初始连接数、最大连接数、最小空闲连接、连接超时时间、空闲连接检测间隔。这些参数没有标准答案,要根据你的数据库承载能力和业务并发量来定。
我的经验值是:初始连接数设为最大连接数的三分之一左右,最大连接数根据数据库的max_connections除以服务实例数再留一些余量。比如数据库最大连接数是 500,你有 5 个服务实例,那每个实例的最大连接数不要超过 80,留出余量给其他操作。
另外,如果你用了多数据源,要确认脚手架的动态数据源切换机制是否还适用。有些脚手架用的是 AOP 切面加 ThreadLocal 的方式切换数据源,这种方式在异步线程里会失效,需要特别注意。
5.3 缓存与消息队列的接入
如果脚手架集成了 Redis 或者消息队列,你要确认它们的序列化方式是否跟你的业务兼容。比如 Redis 的序列化,脚手架可能用的是 JDK 序列化,但你的业务对象如果没有实现 Serializable 接口就会报错。这时候要么改序列化方式为 JSON,要么让你的对象实现序列化接口。
消息队列也是类似,要确认消息的序列化格式、消费者组的命名规则、重试策略这些是否符合你的业务需求。我一般会在改造阶段先把这些中间件的连接信息换成自己的,然后写一个简单的测试接口验证读写是否正常,确认没问题再继续后面的改造。
6. 服务裁剪与业务模块接入:做减法比做加法更重要
6.1 哪些服务可以删,哪些必须留
前面已经区分了框架能力和业务示例,现在到了真正动手删的时候。可以删的服务包括:纯演示用的业务服务、你用不到的监控服务(如果你有自己的监控体系)、用不到的代码生成服务(如果你不打算用它的生成功能)。必须留的服务包括:网关、认证中心、系统管理服务(如果认证依赖它)、以及你业务需要的基础服务。
删服务的时候要注意,先解除依赖再删模块。比如某个业务服务依赖了公共安全模块,你删业务服务的时候,公共安全模块不能跟着删,因为认证中心可能也依赖它。删完之后,在父 pom 里移除对应的<module>声明,然后执行一次构建,确认没有编译错误。
6.2 新增自己的业务模块,遵循同样的分层规范
新增业务模块时,建议直接复制一个现有的业务模块作为模板,然后改名字、改包名、改配置。这样能保证新模块的结构跟脚手架保持一致,减少后期维护成本。新模块的pom.xml里,依赖要按需引入,不要一股脑把公共模块全引进来,否则会导致启动变慢、依赖冲突风险增加。
业务模块的分层通常是 controller、service、mapper、entity、dto、vo 这几层。你要确保自己的代码遵循这个分层,不要把业务逻辑写到 controller 里,也不要在 mapper 里写复杂逻辑。这不是为了好看,而是为了后面排查问题时能快速定位。
6.3 接口文档与网关路由的同步更新
新增业务模块后,要在网关的路由配置里加上对应的路由规则,否则外部请求进不来。路由规则通常包括:路径匹配、目标服务名、过滤器配置。路径匹配要注意不要跟已有路由冲突,比如你用/api/user/**,那就要确认没有别的服务也用了这个前缀。
接口文档也要同步更新。如果脚手架集成了接口文档工具,新模块的接口要能被自动扫描到。有时候需要手动配置扫描路径,这个在模块的配置类里加一行注解就行。更新完路由和文档后,用网关的地址访问一下新接口,确认能通。
7. 改造后的验证与常见问题排查
7.1 分阶段验证,不要一次性全量启动
改造完成后,不要一次性把所有服务都启动起来。我的做法是分三批:第一批启动注册中心和配置中心(如果有),确认基础设施正常;第二批启动网关和认证中心,验证认证流程;第三批启动业务服务,验证业务接口。每批启动后都做一次冒烟测试,确认没问题再启动下一批。
这样做的好处是,一旦出问题,你能快速定位是哪个环节的问题。如果一次性全启动,日志混在一起,排查起来非常痛苦。
7.2 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 服务注册不上 | 注册中心地址配错、网络不通 | 检查配置文件、telnet 注册中心端口 |
| 网关路由 404 | 路由规则没配、服务名不对 | 检查网关配置、确认服务已注册 |
| 认证失败 | 密钥不一致、令牌过期 | 检查网关和认证中心的密钥配置 |
| mapper 注入失败 | 扫描路径没改、包名不对 | 检查启动类上的 MapperScan 注解 |
| 配置不生效 | 配置优先级问题、缓存未刷新 | 检查本地配置和配置中心的优先级 |
7.3 我踩过的几个典型坑
第一个坑是网关的跨域配置。脚手架默认的跨域配置可能只允许了它自己的前端地址,你换成自己的前端地址后,如果忘了改,浏览器会报跨域错误。这个问题的迷惑性在于,后端日志里看不到任何错误,只有浏览器控制台有提示。
第二个坑是令牌有效期。脚手架默认的令牌有效期可能很短,比如 30 分钟,你测试的时候没问题,但实际用的时候用户频繁被踢下线。这个要根据业务场景调整,一般管理后台可以设 2 到 8 小时,移动端可以更长。
第三个坑是数据库字符集。脚手架建表语句可能用的是某种字符集,你换成自己的数据库后,如果字符集不一致,会出现中文乱码。这个在建库的时候就统一好,别等到数据写进去了才发现。
8. 一些让改造更顺手的个人习惯
我在做这类二次开发时,有几个习惯一直保留着。第一个是每改一个模块就提交一次 Git,commit message 写清楚改了什么。这样万一改出问题,回滚很方便,也能通过 diff 看清楚自己动了哪些地方。第二个是维护一个改造清单,把需要改的地方列成 checklist,改完一项勾一项,避免遗漏。第三个是保留一份原始脚手架的代码副本,改造过程中遇到不确定的地方,可以对照原始代码看它是怎么实现的。
另外,改造初期不要追求完美。先把项目跑通,把核心链路走通,然后再逐步优化。我见过有人一上来就重构整个架构,结果改了两周还没跑起来,最后心态崩了。先跑通,再优化,最后才是重构,这个顺序不要颠倒。
还有一点,改造过程中遇到不确定的技术选型,不要自己硬猜。去看脚手架的官方文档,或者看它的源码实现,理解它为什么这么设计。很多时候,脚手架的设计是有原因的,你理解了原因,才知道能不能改、怎么改。盲目替换往往会引入新的问题,而这些问题在后期才暴露出来,排查成本极高。