跳转到内容

启动配置与环境配置

Feat Cloud 里有两类配置很容易混在一起:

  • CloudOptions:启动应用时传给 Feat Cloud 的运行时选项
  • feat.yml / feat-{env}.yml:编译期生成代码时读取的配置输入

这章分成两段:

  • 前半段讲 CloudOptions:启动入口里本次应用怎么跑
  • 后半段讲 Profile:不同环境如何参与编译期生成

核心判断很简单:启动入口里本次怎么跑,看 CloudOptions;会影响生成代码的环境差异,看 feat.yml

这一部分只处理启动入口里的运行时选项:扫描哪些包、提前注册哪些对象、是否托管静态资源。

CloudOptions 继承自 ServerOptions,所以端口、线程数、调试模式、HTTPS 这类基础能力仍然在 ServerOptions 里配置。

遇到这些问题时,通常看 CloudOptions

  • 控制器已经写好,但你想限制哪些包会被接受
  • 有对象必须在 Feat Cloud 容器启动前就准备好
  • 应用需要顺手托管 classpath 或文件系统里的静态资源
  • 本次启动要临时调整端口、debug 或 Router

遇到这些问题时,通常看 feat.yml 或 Profile:

  • 不同环境使用不同端口、数据库或 Redis
  • Session 存储方式在本地和生产不同
  • MyBatis、Redis 等扩展需要参与编译期生成
  • 修改后需要重新构建才生效
配置项方法典型用途
包扫描setPackages(String...)控制 Controller / Bean 的扫描范围
外部 BeanregisterBean(String, Object)把启动前已有对象注册进容器
静态资源setStaticLocations(String)托管类路径或本地目录里的静态文件

如果你的启动类和控制器不在同一个合理的根包下,或者你只想限制扫描范围,可以显式设置包路径。

Bootstrap.java
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(...)

有些对象不是通过 Feat Cloud 自己创建的,比如数据源、第三方客户端、配置对象。这时可以在启动时手动注册。

ExternalBeanDemo.java
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

重复注册同名 Bean 会失败
options.registerBean("dataSource", ds1);
options.registerBean("dataSource", ds2); // 会失败

Feat Cloud 默认静态资源目录是 classpath:static。如果你项目里有前端页面、上传后的公开文件或静态文档,可以直接改位置。

StaticLocationDemo.java
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 返回。

实际项目里,这几个配置项经常一起出现:

CloudOptionsDemo.java
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 的环境配置,也不会改变已经生成好的代码结构。

从这里开始,关注点从“本次启动怎么跑”切到“不同环境如何生成代码”。

简笔手绘卡通风格,中央是默认 feat.yml 配置卡片,分叉到 dev、test、prod 三张环境卡片,环境卡片与默认卡片像透明纸叠加,表现深度合并和环境覆盖,900x383,绿色高亮合并路径

业务应用很少只有一个运行环境。本地开发可能连本机数据库,测试环境要打开更多日志,生产环境则使用独立的数据库、Redis 和端口。

默认配置文件有两种写法:

  • feat.yml
  • feat.yaml

环境配置文件也有两种写法:

  • feat-dev.yml
  • feat-dev.yaml
  • feat-prod.yml
  • feat-prod.yaml

其中 devprod 就是环境名。环境名只能包含字母和数字,例如 devtestprodgray1

一个典型项目可以这样组织资源文件:

src/main/resources
src/main/resources/
├── feat.yml
├── feat-dev.yml
└── feat-prod.yml

feat.yml 放所有环境都共享的默认值:

feat.yml
server:
port: 8080
debug: false
feat:
datasource:
url: jdbc:mysql://localhost:3306/app
username: app
password: app

feat-dev.yml 只写开发环境要覆盖的内容:

feat-dev.yml
server:
port: 8081
feat:
datasource:
url: jdbc:mysql://localhost:3306/app_dev
username: dev
password: dev

Feat Cloud 会先读取默认配置,再读取环境配置。

dev 环境来说,顺序是:

  1. 读取 feat.yml
  2. 读取 feat-dev.yml
  3. feat-dev.yml 的内容覆盖到默认配置上

覆盖粒度遵循一个规则:对象深度合并,普通值和数组整体替换。

feat.yml
server:
port: 8080
debug: false
host: 0.0.0.0
feat-dev.yml
server:
port: 8081

合并后,server.port 会被覆盖,而 debughost 会继续保留:

dev 环境最终配置
server:
port: 8081
debug: false
host: 0.0.0.0

如果环境文件里的值是数组,则以环境配置为准,不会和默认数组拼接。

如果项目里只有 feat.yml,Feat Cloud 只会生成默认环境的一套代码。

如果同时存在 feat-dev.ymlfeat-prod.yml,Feat Cloud 会为每个环境生成一套完整代码:

配置文件生成类名示例
feat.ymlFeatApplication
feat-dev.ymlFeatApplicationDev
feat-prod.ymlFeatApplicationProd

Controller、Bean、Mapper 等生成类也会追加同样的环境后缀。这样做是为了让每个环境都有独立的配置结果,同时避免生成类同名冲突。

打包后的应用里可能已经包含多套环境代码。启动时通过 feat.profiles.active 选择其中一套:

使用 JVM 参数
java -Dfeat.profiles.active=dev -jar yourapp.jar

也可以使用环境变量:

使用环境变量
FEAT_PROFILES_ACTIVE=prod java -jar yourapp.jar

如果两者都没有设置,Feat Cloud 会使用默认环境,也就是 feat.yml 对应的那套生成结果。

适合拆分的内容通常有这些:

  • 端口
  • 数据库地址
  • Redis 地址
  • 会话存储方式
  • 第三方服务地址
  • 只在某个环境启用的 Feat Cloud 扩展配置

不建议把业务开关、灰度策略或用户级动态规则放进 feat-{env}.yml。这些配置更适合放进数据库、配置中心或业务自己的配置系统,因为它们通常需要运行时调整。

多环境配置影响的是编译期生成结果。如果你修改了 feat-dev.yml,只重启已经打好的 jar 不会生效,需要重新执行构建。

重新生成代码并打包
mvn clean package

这也是 Feat Cloud profile 与传统运行期 profile 最大的区别:它追求的是启动时少扫描、少反射、少动态决策,而不是运行时随时重载配置。