If you’ve spent time with Spring Boot, you know the spring.profiles.active routine — one property, multiple application-{profile}.properties files, and a creeping suspicion that somewhere a prod config is silently falling back to dev defaults.
Solon’s answer is solon.env. Same concept, different design choices — and one pattern (solon.env.on in-file segments) that Spring has no direct equivalent for.
Here’s how it works.
The Single Key: solon.env
Everything in Solon’s multi-environment system pivots on one property:
# app.yml
solon.env: dev
Enter fullscreen mode Exit fullscreen mode
Set this, and Solon automatically loads app-dev.yml alongside app.yml. Remove it, and only app.yml loads. No profiles API, no @ActiveProfiles, no annotation scanning — just a key.
Environment-Specific Files
The naming convention is app-{env}.yml (or .properties):
app.yml ← always loaded
app-dev.yml ← loaded when solon.env=dev
app-pro.yml ← loaded when solon.env=pro
app-test.yml ← loaded when solon.env=test
Enter fullscreen mode Exit fullscreen mode
Keys in app-{env}.yml override the same keys in app.yml. Everything else in app.yml is inherited.
One constraint worth knowing: app-{env}.yml cannot itself declare another solon.env. The env chain is intentionally one level deep.
Four Ways to Activate an Environment
You don’t have to hardcode solon.env in the file. You can set it at any of four levels, with higher numbers winning:
app.yml
solon.env: dev
2
JVM system property
-Dsolon.env=pro
3
Launch argument
--env=pro
4 (highest)
OS environment variable
export solon.env=pro
In practice: set solon.env: dev in app.yml as a safe default for local development, then override at deploy time:
# Jar deployment
java -jar demo.jar --env=pro
# Docker run
docker run -e 'solon.env=pro' demo_image
# Kubernetes pod spec
env:
- name: solon.env
value: pro
Enter fullscreen mode Exit fullscreen mode
All three launch forms below are equivalent:
java -Dsolon.env=pro -jar demo.jar
java -jar demo.jar --solon.env=pro
java -jar demo.jar --env=pro
Enter fullscreen mode Exit fullscreen mode
Six Loading Tiers
solon.env controls which env file loads, but that’s just the first two tiers of a six-tier config loading stack. Later tiers override earlier ones:
app.yml + app-{env}.yml
Base config, inside the jar
2
solon.config.load classpath entries
v2.2.7+, supports ${solon.env} variables; wildcard * from v2.7.6+
3
solon.config.add external files
Files placed beside the jar at runtime
4
JVM props / env vars / launch args
Runtime overrides
5
app.cfg().loadAdd() in startup code
Programmatic additions
6
Solon Cloud Config (e.g., Nacos)
Remote config center
The pattern: static defaults live inside the jar, runtime overrides live outside it, remote config sits at the top.
Loading More Configs: solon.config.load
For apps with per-module configs, solon.config.load lets you declare additional classpath resources with variable substitution and wildcard support (v2.7.6+):
# app.yml
solon.env: dev
solon.config.load:
- "classpath:${solon.env}/jdbc.yml" # resolves to classpath:dev/jdbc.yml
- "classpath:${solon.env}/*.yml" # all yml in dev/ folder (v2.7.6+)
- "classpath:app-ds-${solon.env}.yml" # e.g., app-ds-dev.yml
- "classpath:common/base.yml" # environment-agnostic shared config
Enter fullscreen mode Exit fullscreen mode
This lets you split config by concern (database, cache, auth) rather than cramming everything into one file per environment.
For external files placed beside the jar, use solon.config.add:
solon.config.add: "./local-overrides.yml"
Enter fullscreen mode Exit fullscreen mode
Or at launch time:
java -jar demo.jar --solon.config.add=./local-overrides.yml
Enter fullscreen mode Exit fullscreen mode
The In-File Segment Trick (v2.5.5+)
This is the feature that has no Spring Boot equivalent. Solon lets you embed multiple environment-specific segments inside a single app.yml, separated by --- and guarded by solon.env.on:
solon.env: pro
---
solon.env.on: pro
demo.auth:
user: root
password: "${AUTH_PASSWORD}"
---
solon.env.on: dev|test
demo.auth:
user: demo
password: "demo1234"
Enter fullscreen mode Exit fullscreen mode
Only the segment matching the current solon.env is applied. The | syntax lets one segment cover multiple environments.
This pattern suits small services where maintaining separate env files feels heavy, but you still want clear boundaries in the source.
Programmatic Loading
Sometimes you need to load config based on conditions known only at startup. Two clean hooks:
@SolonMain
public class App {
public static void main(String[] args) {
Solon.start(App.class, args, app -> {
// Tier 5: added before beans initialize, merged into the config stack
app.cfg().loadAdd("app-jdbc-" + app.cfg().env() + ".yml");
app.cfg().loadAdd("app-cache-" + app.cfg().env() + ".yml");
});
}
}
Enter fullscreen mode Exit fullscreen mode
Or via @Import on the startup class:
@Import(profiles = "classpath:module-defaults.yml")
@SolonMain
public class App {
public static void main(String[] args) {
Solon.start(App.class, args);
}
}
Enter fullscreen mode Exit fullscreen mode
For Docker/Kubernetes, loadEnv() pulls in environment variables by prefix:
Solon.start(App.class, args, app -> {
app.cfg().loadEnv("demo."); // pulls in all env vars starting with "demo."
});
Enter fullscreen mode Exit fullscreen mode
Note: Solon automatically syncs solon.* environment variables into the config store, so solon.env=pro as a container env var works without loadEnv().
Injecting Config Values
@Inject with ${} syntax wires config values into beans:
@Component
public class DataSourceConfig {
@Inject("${db.url}")
private String dbUrl;
@Inject("${db.username}")
private String username;
}
Enter fullscreen mode Exit fullscreen mode
For structured config objects, @BindProps binds an entire prefix:
@BindProps(prefix = "app.mail")
@Component
public class MailProperties {
public String host;
public int port;
public String username;
}
Enter fullscreen mode Exit fullscreen mode
Variable references also work inside YAML values themselves:
db.server: "10.0.0.1"
db.name: "orders"
db.url: "jdbc:mysql://${db.server}/${db.name}" # composed from other keys
Enter fullscreen mode Exit fullscreen mode
A Realistic Project Layout
src/main/resources/
├── app.yml # base config + solon.env: dev
├── app-dev.yml # dev overrides
├── app-pro.yml # prod overrides
└── config/
├── common/
│ └── base.yml # shared constants (env-agnostic)
├── dev/
│ └── jdbc.yml # dev DB connection
└── pro/
└── jdbc.yml # prod DB connection
Enter fullscreen mode Exit fullscreen mode
app.yml:
solon.env: dev
solon.config.load:
- "classpath:config/common/base.yml"
- "classpath:config/${solon.env}/jdbc.yml"
solon.app.name: "order-service"
server.port: 8080
Enter fullscreen mode Exit fullscreen mode
app-dev.yml:
solon.logging.level: debug
solon.debug: 1
Enter fullscreen mode Exit fullscreen mode
app-pro.yml:
solon.logging.level: warn
server.port: 80
Enter fullscreen mode Exit fullscreen mode
Deploy to prod:
java -jar order-service.jar --env=pro
Enter fullscreen mode Exit fullscreen mode
One flag flips the entire config stack.
Migration Reference (from Spring Boot)
Spring Boot Solon Notesspring.profiles.active
solon.env
Same concept, different key
application-{profile}.properties
app-{env}.yml
Same convention
@Profile("dev") on beans
solon.env.on: dev in YAML
Config-layer isolation vs code-layer
@PropertySource
solon.config.load in YAML
Declarative import
@ConfigurationProperties(prefix="x")
@BindProps(prefix="x")
Prefix binding
@Value("${x}")
@Inject("${x}")
Value injection
The mental model is nearly identical. The key difference: Solon keeps environment logic in config (via solon.env.on segments or separate files) rather than scattering @Profile annotations across bean definitions.
Key Takeaways
-
solon.envis the single activation key — set it in the file, at the JVM, or as a container env var -
app-{env}.ymlis loaded automatically; no registration step required - Six loading tiers let you layer static defaults with runtime overrides cleanly
-
solon.config.loadwith${solon.env}handles per-module, per-environment config splitting - In-file
---segments withsolon.env.on(v2.5.5+) are unique to Solon — useful for small services - Config injection uses
@Inject("${...}")and@BindProps— both standard Solon IoC
The loading order is deterministic and documented. Deploy with confidence.
답글 남기기