启动配置与环境配置
Feat Cloud 里有两类配置很容易混在一起:
CloudOptions:启动应用时传给 Feat Cloud 的运行时选项feat.yml/feat-{env}.yml:编译期生成代码时读取的配置输入
这章分成两段:
- 前半段讲
CloudOptions:启动入口里本次应用怎么跑 - 后半段讲 Profile:不同环境如何参与编译期生成
核心判断很简单:启动入口里本次怎么跑,看 CloudOptions;会影响生成代码的环境差异,看 feat.yml。
CloudOptions 启动配置
Section titled “CloudOptions 启动配置”这一部分只处理启动入口里的运行时选项:扫描哪些包、提前注册哪些对象、是否托管静态资源。
CloudOptions 继承自 ServerOptions,所以端口、线程数、调试模式、HTTPS 这类基础能力仍然在 ServerOptions 里配置。
遇到这些问题时,通常看 CloudOptions:
- 控制器已经写好,但你想限制哪些包会被接受
- 有对象必须在 Feat Cloud 容器启动前就准备好
- 应用需要顺手托管
classpath或文件系统里的静态资源 - 本次启动要临时调整端口、debug 或 Router
遇到这些问题时,通常看 feat.yml 或 Profile:
- 不同环境使用不同端口、数据库或 Redis
- Session 存储方式在本地和生产不同
- MyBatis、Redis 等扩展需要参与编译期生成
- 修改后需要重新构建才生效
启动时最常碰到的三件事
Section titled “启动时最常碰到的三件事”| 配置项 | 方法 | 典型用途 |
|---|---|---|
| 包扫描 | setPackages(String...) | 控制 Controller / Bean 的扫描范围 |
| 外部 Bean | registerBean(String, Object) | 把启动前已有对象注册进容器 |
| 静态资源 | setStaticLocations(String) | 托管类路径或本地目录里的静态文件 |
收窄扫描边界
Section titled “收窄扫描边界”如果你的启动类和控制器不在同一个合理的根包下,或者你只想限制扫描范围,可以显式设置包路径。
import tech.smartboot.feat.cloud.FeatCloud;
public class Bootstrap { public static void main(String[] args) { FeatCloud.cloudServer(options -> { options.setPackages( "com.example.controller", "com.example.service" ); }).listen(); }}什么时候应该用它:
- 多模块项目里,控制器不在启动类同包或子包
- 你只想扫描少量包,提高启动阶段的可控性
- 你要避免把不相关 demo 或测试类扫进来
如果启动类本身已经位于合理根包下,很多情况下不需要写 setPackages(...)。
把已有对象交给容器
Section titled “把已有对象交给容器”有些对象不是通过 Feat Cloud 自己创建的,比如数据源、第三方客户端、配置对象。这时可以在启动时手动注册。
import tech.smartboot.feat.cloud.FeatCloud;
public class ExternalBeanDemo { public static void main(String[] args) { FeatCloud.cloudServer(options -> { options.registerBean("dataSource", createDataSource()); options.registerBean("buildVersion", "1.0.0"); }).listen(); }
private static Object createDataSource() { return new Object(); }}registerBean(...) 的 key 不能重复。源码里如果发现同名 key,会直接抛 FeatException。
options.registerBean("dataSource", ds1);options.registerBean("dataSource", ds2); // 会失败托管静态资源
Section titled “托管静态资源”Feat Cloud 默认静态资源目录是 classpath:static。如果你项目里有前端页面、上传后的公开文件或静态文档,可以直接改位置。
import tech.smartboot.feat.cloud.FeatCloud;
public class StaticLocationDemo { public static void main(String[] args) { FeatCloud.cloudServer(options -> { options.setStaticLocations("classpath:public"); }).listen(); }}你可以使用两种路径形式:
options.setStaticLocations("classpath:static");options.setStaticLocations("classpath:public");options.setStaticLocations("/var/www/static");options.setStaticLocations("./public");当你配置静态资源目录后,Feat Cloud 会使用静态资源处理器提供目录首页、常见文件类型识别、Last-Modified 缓存和 404 返回。
把启动策略写在入口
Section titled “把启动策略写在入口”实际项目里,这几个配置项经常一起出现:
import tech.smartboot.feat.cloud.FeatCloud;
public class CloudOptionsDemo { public static void main(String[] args) { FeatCloud.cloudServer(options -> { options.port(8080); options.debug(true);
options.setPackages("com.example.controller", "com.example.service"); options.registerBean("dataSource", createDataSource()); options.setStaticLocations("classpath:public"); }).listen(); }
private static Object createDataSource() { return new Object(); }}这段代码表达的是本次应用如何启动。它不会替代 feat.yml 的环境配置,也不会改变已经生成好的代码结构。
Profile 环境配置
Section titled “Profile 环境配置”从这里开始,关注点从“本次启动怎么跑”切到“不同环境如何生成代码”。

业务应用很少只有一个运行环境。本地开发可能连本机数据库,测试环境要打开更多日志,生产环境则使用独立的数据库、Redis 和端口。
默认配置文件有两种写法:
feat.ymlfeat.yaml
环境配置文件也有两种写法:
feat-dev.ymlfeat-dev.yamlfeat-prod.ymlfeat-prod.yaml
其中 dev、prod 就是环境名。环境名只能包含字母和数字,例如 dev、test、prod、gray1。
最小可用结构
Section titled “最小可用结构”一个典型项目可以这样组织资源文件:
src/main/resources/├── feat.yml├── feat-dev.yml└── feat-prod.ymlfeat.yml 放所有环境都共享的默认值:
server: port: 8080 debug: false
feat: datasource: url: jdbc:mysql://localhost:3306/app username: app password: appfeat-dev.yml 只写开发环境要覆盖的内容:
server: port: 8081
feat: datasource: url: jdbc:mysql://localhost:3306/app_dev username: dev password: devFeat Cloud 会先读取默认配置,再读取环境配置。
对 dev 环境来说,顺序是:
- 读取
feat.yml - 读取
feat-dev.yml - 将
feat-dev.yml的内容覆盖到默认配置上
覆盖粒度遵循一个规则:对象深度合并,普通值和数组整体替换。
server: port: 8080 debug: false host: 0.0.0.0server: port: 8081合并后,server.port 会被覆盖,而 debug 和 host 会继续保留:
server: port: 8081 debug: false host: 0.0.0.0如果环境文件里的值是数组,则以环境配置为准,不会和默认数组拼接。
编译期会生成什么
Section titled “编译期会生成什么”如果项目里只有 feat.yml,Feat Cloud 只会生成默认环境的一套代码。
如果同时存在 feat-dev.yml、feat-prod.yml,Feat Cloud 会为每个环境生成一套完整代码:
| 配置文件 | 生成类名示例 |
|---|---|
feat.yml | FeatApplication |
feat-dev.yml | FeatApplicationDev |
feat-prod.yml | FeatApplicationProd |
Controller、Bean、Mapper 等生成类也会追加同样的环境后缀。这样做是为了让每个环境都有独立的配置结果,同时避免生成类同名冲突。
运行时如何选择环境
Section titled “运行时如何选择环境”打包后的应用里可能已经包含多套环境代码。启动时通过 feat.profiles.active 选择其中一套:
java -Dfeat.profiles.active=dev -jar yourapp.jar也可以使用环境变量:
FEAT_PROFILES_ACTIVE=prod java -jar yourapp.jar如果两者都没有设置,Feat Cloud 会使用默认环境,也就是 feat.yml 对应的那套生成结果。
什么时候应该拆环境配置
Section titled “什么时候应该拆环境配置”适合拆分的内容通常有这些:
- 端口
- 数据库地址
- Redis 地址
- 会话存储方式
- 第三方服务地址
- 只在某个环境启用的 Feat Cloud 扩展配置
不建议把业务开关、灰度策略或用户级动态规则放进 feat-{env}.yml。这些配置更适合放进数据库、配置中心或业务自己的配置系统,因为它们通常需要运行时调整。
重新构建才会生效
Section titled “重新构建才会生效”多环境配置影响的是编译期生成结果。如果你修改了 feat-dev.yml,只重启已经打好的 jar 不会生效,需要重新执行构建。
mvn clean package这也是 Feat Cloud profile 与传统运行期 profile 最大的区别:它追求的是启动时少扫描、少反射、少动态决策,而不是运行时随时重载配置。