This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/docs by this push:
new 39c8443740 docs: add @Value annotation topic page + 9.5.0 release
notes for TODO-79
39c8443740 is described below
commit 39c844374081849367283306c7121259d8a29eea
Author: James Bognar <[email protected]>
AuthorDate: Mon May 25 14:51:56 2026 -0400
docs: add @Value annotation topic page + 9.5.0 release notes for TODO-79
Co-authored-by: Cursor <[email protected]>
---
pages/release-notes/9.5.0.md | 82 ++++++++-
.../02.21.01.SimpleVariableLanguageBasics.md | 18 ++
pages/topics/02.21.06.ValueAnnotationBasics.md | 185 +++++++++++++++++++++
pages/topics/05.04.01.VariableBasics.md | 7 +
sidebars.ts | 5 +
5 files changed, 296 insertions(+), 1 deletion(-)
diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index 36e453e77c..a5cbdfde0e 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -6,7 +6,7 @@ title: "Release 9.5.0"
**Date:** TBD
-Juneau 9.5.0 is a minor release with native OpenAPI 3.1 emission (alongside
Swagger v2, composed via the new four-class `org.apache.juneau.rest.docs` mixin
pack — `BasicSwaggerResource` / `BasicSwaggerUiResource` /
`BasicOpenApiResource` / `BasicRedocResource` — that replaces the
previously-considered `apiFormat` string knob), native TOML and YAML support,
BSON (Binary JSON) support for MongoDB-interoperable binary serialization, CBOR
(Concise Binary Object Representation) per RFC 8949 f [...]
+Juneau 9.5.0 is a minor release with native OpenAPI 3.1 emission (alongside
Swagger v2, composed via the new four-class `org.apache.juneau.rest.docs` mixin
pack — `BasicSwaggerResource` / `BasicSwaggerUiResource` /
`BasicOpenApiResource` / `BasicRedocResource` — that replaces the
previously-considered `apiFormat` string knob), native TOML and YAML support,
BSON (Binary JSON) support for MongoDB-interoperable binary serialization, CBOR
(Concise Binary Object Representation) per RFC 8949 f [...]
### juneau-marshall
@@ -387,8 +387,52 @@ land, parser sessions other than `JsonParserSession` /
`Json5ParserSession` prod
### juneau-commons
+#### `@Value` annotation + `${xxx}` shortcut (TODO-79)
+
+- Added `org.apache.juneau.commons.inject.Value` annotation for declarative
injection of configuration
+ values into `BeanInstantiator`-created beans (fields, constructor
parameters, setter parameters,
+ arbitrary method parameters). Resolves expressions through
`VarResolver.DEFAULT` and coerces
+ the resolved string to the target type via `Settings.toType(...)`.
+- Extended `JsrSupport` with FQN-based detection of the new Juneau `@Value`
(`JUNEAU_VALUE`) and
+ Spring's `@Value` (`SPRING_VALUE =
"org.springframework.beans.factory.annotation.Value"`), keeping
+ `juneau-commons` compile-time clean of Spring. Either annotation is picked
up by the same
+ injection path.
+- Made `@Value` and `@Inject` mutually exclusive on the same site — applying
both throws
+ `BeanCreationException` with a clear "site already has @Value / @Inject"
message.
+- Added the `${xxx}` shortcut in `VarResolverSession` — the tokenizer rewrites
`${...}` to `$P{...}`
+ before resolution, so it inherits the full `PropertyVar` source stack
(system properties, env vars,
+ registered `Config`s, Spring `Environment`). The first top-level `:` in the
expression is
+ translated to `,` so Spring-style `${key:default}` defaulting works without
further changes.
+- Made `Settings.toType(...)` public and extended it to also recognise `static
T parse(CharSequence)`
+ methods (in addition to `parse(String)`), so `java.time` types like
`Instant` coerce cleanly.
+- Added `Settings.removeSource(PropertySource)` for explicit deregistration —
required so per-context
+ bridges (microservice, `RestContext`, Spring `Environment`) can clean up at
shutdown without
+ cross-test bleed.
+- `Optional<T>` field/parameter injection collapses `null` and empty-string
results to
+ `Optional.empty()` — matching the "missing-key" intuition that consumers
expect.
+
### juneau-config
+#### Classpath-default `Config` bridge to `Settings` (TODO-79)
+
+- Added `org.apache.juneau.config.ConfigPropertySourceProvider`, a
`META-INF/services`-discovered
+ `PropertySourceProvider` that bridges `Config.getSystemDefault()` (i.e. the
classpath
+ `juneau.cfg` / `default.cfg` / `application.cfg` / etc. discovered by
+ `Config.getCandidateSystemDefaultConfigNames()`) into the process-wide
`Settings` singleton.
+- Resolution is **lazy** — `Config.getSystemDefault()` is invoked on the first
`Settings.get(name)`
+ lookup rather than at SPI registration time. This avoids a
static-initialization cycle
+ (`Settings.<clinit>` → `useServiceLoader()` →
`ConfigPropertySourceProvider.create()` →
+ `Config.<clinit>` → `Cache.create()` → `Utils.env(...)` →
`Settings.<clinit>`) and a runtime
+ reentrance cycle (`Config.findSystemDefault()` looking up
`"juneau.configFile"` through every
+ registered source, including this one). A thread-local guard short-circuits
the reentrant
+ lookup; any throwable while `Config` is mid-initialisation is treated as
"not yet available"
+ without memoising the failure.
+- The provider is **silent** when no classpath `Config` exists — every lookup
returns
+ `missing()`, no log noise, no exception.
+- The provider's `order()` is `100`, deliberately low — so any programmatic
+ `Settings.get().addSource(...)` registered later (microservice,
`RestContext`, Spring) shadows
+ the classpath default at lookup time.
+
#### YAML config format support (TODO-32)
- Added optional YAML config support to `juneau-config` alongside the existing
INI/CFG format.
@@ -1986,6 +2030,16 @@ String name
### juneau-rest-server
+#### `RestContext` wires its `Config` into `Settings` (TODO-79)
+
+- `RestContext`'s constructor now installs a `ConfigPropertySource` for the
resource-scoped `Config`
+ onto the process-wide `Settings.get()` source stack, and `destroy()` removes
it. This lets
+ `@Value("${section/key}")` injections on resource-scoped beans see the same
`Config` that
+ `$C{section/key}` resolves against — without any further wiring.
+- Because `Settings` walks sources in reverse insertion order, the
per-resource `Config` wins over
+ the classpath-default `juneau.cfg` bridge installed by
`ConfigPropertySourceProvider` and over
+ the JVM-default sources (system properties, env vars).
+
#### Rest Debug Rethink (TODO-20)
`juneau-rest-server` now resolves debug behavior through a single
`DebugConfig` model and typed `@Debug` annotation slot, replacing the previous
split model (`debug`, `debugDefault`, `debugEnablement`, `debugOn`) on `@Rest`
/ `@RestOp`.
@@ -2795,6 +2849,14 @@ See [REST Server — Conditional-GET / ETag
Helpers](/docs/topics/RestServerCond
### juneau-microservice (formerly juneau-microservice-core)
+#### Per-microservice `Config` wired into `Settings` (TODO-79)
+
+- `Microservice`'s constructor now installs a `ConfigPropertySource` for the
microservice's own
+ `Config` onto `Settings.get()`, and `stop()` removes it. Any
`@Value("${section/key}")`
+ injection — on REST resources mounted under the microservice, on listeners,
or on any other
+ bean built through `BeanInstantiator` — resolves against the microservice
config without an
+ explicit lookup call.
+
#### Module Renamed (TODO-36)
The `juneau-microservice-core` Maven artifact has been **renamed** to
`juneau-microservice`. The aggregator pom is now
@@ -2869,6 +2931,24 @@ microservices that never call `.configurations(...)`
behave identically to pre-9
See <a href="/docs/topics/MicroserviceCoreInject">Inject-Aware
Microservice</a> for the full guide.
+### juneau-rest-server-springboot
+
+#### Spring `Environment` bridge — `@Value` reads `application.yaml` (TODO-79)
+
+- Added `org.apache.juneau.rest.springboot.SpringEnvironmentPropertySource` —
a `PropertySource`
+ that reads from a Spring `Environment` so `@Value("${spring.app.key}")`
injections on
+ Juneau-instantiated beans resolve against the same property sources Spring
Boot itself uses
+ (`application.yaml` / `application.properties`, command-line args, env vars,
`@PropertySource`,
+ etc.).
+- `SpringBeanStore`'s constructor now auto-registers a lazy bridge against the
+ `ApplicationContext`'s `Environment`, and `SpringBeanStore.clear()` removes
it. The bridge is
+ resolved lazily — the constructor doesn't actually call
`appContext.getEnvironment()` until the
+ first lookup — so callers that mock the `ApplicationContext` without
stubbing `getEnvironment()`
+ (e.g. unit tests that only exercise bean-lookup paths) are unaffected.
+- The bridge sits *above* the classpath-default `juneau.cfg` bridge in the
`Settings` source stack
+ (later insertion wins), so `application.yaml` shadows `juneau.cfg` when both
define the same
+ key — matching the typical Spring Boot precedence intuition.
+
### juneau-microservice-jetty
#### `HealthProbeConfiguration` (TODO-65)
diff --git a/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
b/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
index 98af55b737..7f240d5816 100644
--- a/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
+++ b/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
@@ -55,3 +55,21 @@ The following shows how variables can be arbitrarily
nested...
// 3) 'not found' string if system property not found.
String property =
VarResolver.DEFAULT.resolve("$E{MYPROPERTY,$S{my.property,not found}}");
```
+
+## The `${xxx}` shortcut (9.5.0)
+
+Starting in 9.5.0 the tokenizer recognises `${xxx}` as a shorthand for
`$P{xxx}` — the
+<a href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html"
target="_blank">PropertyVar</a>
+that resolves against the unified `Settings` source stack (system properties,
env vars,
+classpath `juneau.cfg`, per-microservice `Config`, Spring `Environment`, …). A
Spring-style
+default after a colon works because the first top-level `:` is rewritten to
`,` before
+delegation to `PropertyVar`:
+
+```java
+// Equivalent forms:
+VarResolver.DEFAULT.resolve("$P{my.key,fallback}");
+VarResolver.DEFAULT.resolve("${my.key:fallback}");
+```
+
+See [@Value Annotation Basics](./ValueAnnotationBasics.md) for the broader
picture of how
+`${...}` ties into declarative configuration injection and Spring Boot interop.
diff --git a/pages/topics/02.21.06.ValueAnnotationBasics.md
b/pages/topics/02.21.06.ValueAnnotationBasics.md
new file mode 100644
index 0000000000..2c062d88b5
--- /dev/null
+++ b/pages/topics/02.21.06.ValueAnnotationBasics.md
@@ -0,0 +1,185 @@
+---
+title: "@Value Annotation Basics"
+slug: ValueAnnotationBasics
+---
+
+The <a href="/site/apidocs/org/apache/juneau/commons/inject/Value.html"
target="_blank">@Value</a> annotation
+(introduced in 9.5.0, package `org.apache.juneau.commons.inject`) is a
declarative way to inject configuration
+values into beans created by <a
href="/site/apidocs/org/apache/juneau/commons/inject/BeanInstantiator.html"
target="_blank">BeanInstantiator</a> — constructor parameters, setter
parameters, and fields.
+
+It complements <a
href="/site/apidocs/org/apache/juneau/commons/inject/Inject.html"
target="_blank">@Inject</a>:
+
+- `@Inject` requests a *bean* from the bean store by type.
+- `@Value` requests a *configuration value* from <a
href="/site/apidocs/org/apache/juneau/commons/settings/Settings.html"
target="_blank">Settings</a> via the
+ <a
href="/site/apidocs/org/apache/juneau/commons/svl/VarResolver.html#DEFAULT"
target="_blank">VarResolver.DEFAULT</a> resolver.
+
+The two are mutually exclusive on the same injection site — applying both
raises
+`BeanCreationException`.
+
+## The `${...}` shortcut
+
+Inside a `@Value` expression (or anywhere you can write SVL), `${key}` is
shorthand for `$P{key}` —
+the <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html"
target="_blank">PropertyVar</a> that
+looks up a unified property by name from `Settings`. The shortcut also accepts
a Spring-style
+default after a colon:
+
+```text
+${some.key} — required key, missing → empty string
+${some.key:fallback} — default-when-missing
+${section/sub.key} — nested property names (Config sections, etc.)
+${a.b:${other:none}} — nested expansion (defaults composed)
+```
+
+This is purely surface syntax — the tokenizer rewrites `${...}` to `$P{...}`
before resolution.
+Everything `$P{...}` does (recursive resolution, default values, env-var
fallback through chained
+sources) works identically with `${...}`. See
+[Simple Variable Language Basics](./SimpleVariableLanguageBasics.md) and
+[Variable Basics](./VariableBasics.md) for the underlying mechanics.
+
+## Resolution order (precedence)
+
+`Settings.get(name)` walks every registered `PropertySource` in **reverse
insertion order** — the
+most-recently-added source wins. The default chain (built up at startup) looks
like:
+
+1. **`PropertySourceProvider` SPI sources** added at process startup (e.g.
classpath `juneau.cfg`
+ via `ConfigPropertySourceProvider`). These register first and therefore sit
at the *bottom* of
+ the priority list.
+2. **Per-microservice `Config`** —
`org.apache.juneau.microservice.Microservice` installs a
+ `ConfigPropertySource` for its own `Config` in its constructor.
+3. **Per-`RestContext` `Config`** — `RestContext` installs one for the
`@Rest`-resource-scoped
+ `Config` in its constructor.
+4. **Spring `Environment` bridge** — when `juneau-rest-server-springboot`'s
`SpringBeanStore` is
+ constructed, it installs a `SpringEnvironmentPropertySource` so
`${spring.app.key}` resolves
+ against `application.yaml`, env vars, command-line args, and any custom
Spring `PropertySource`.
+5. **Built-in `Settings` sources** — system properties, environment variables,
`.env` file,
+ argument list, JAR manifest entries.
+
+Because all sources are checked, the same `@Value` expression resolves
identically under a
+microservice, a Spring Boot app, or a bare REST resource — the *origin* of the
value changes, the
+expression doesn't.
+
+## Field injection
+
+```java
+import org.apache.juneau.commons.inject.Value;
+
+public class MyResource {
+
+ @Value("${myapp.title:Untitled}")
+ private String title;
+
+ @Value("${myapp.maxPageSize:50}")
+ private int maxPageSize;
+}
+```
+
+`Settings.toType(...)` coerces the resolved string to the target type.
Coercion supports:
+
+- Primitives and their boxed wrappers.
+- `java.time` types (`Instant`, `Duration`, `Period`, `LocalDate`, etc.) — any
type with a
+ `parse(CharSequence)` or `parse(String)` static method.
+- `Optional<T>` — `Optional.empty()` is injected when the key resolves to
`null` or the empty
+ string; otherwise `Optional.of(coerced)`.
+- Any type with a single-string-argument constructor.
+
+## Constructor + setter parameter injection
+
+`@Value` works on constructor parameters and on setter (and any method)
parameters:
+
+```java
+public class JwtValidator {
+
+ private final Duration ttl;
+
+ public JwtValidator(@Value("${jwt.cache.ttl:PT5M}") Duration ttl) {
+ this.ttl = ttl;
+ }
+
+ @Inject
+ public void setKeyResolver(KeyResolver keys) { ... } // @Inject path —
bean lookup
+
+ public void setMaxClock(@Value("${jwt.clock.skew:PT30S}") Duration skew) {
... }
+}
+```
+
+## Spring `@Value` compatibility
+
+For consumers migrating from Spring Boot,
`org.springframework.beans.factory.annotation.Value`
+is detected by *fully-qualified name* (`JsrSupport.SPRING_VALUE`) —
`juneau-commons` doesn't
+declare a compile-time dependency on Spring. The same resolution chain
applies, so the following
+two beans are functionally identical inside a Juneau bean store:
+
+```java
+public class A {
+ @org.apache.juneau.commons.inject.Value("${jwt.cache.ttl:PT5M}")
+ Duration ttl;
+}
+
+public class B {
+
@org.springframework.beans.factory.annotation.Value("${jwt.cache.ttl:PT5M}")
+ Duration ttl;
+}
+```
+
+## Spring Boot bridge (`juneau-rest-server-springboot`)
+
+Inside a Spring Boot application, `SpringBeanStore` automatically installs a
+<a
href="/site/apidocs/org/apache/juneau/rest/springboot/SpringEnvironmentPropertySource.html"
target="_blank">SpringEnvironmentPropertySource</a>
+the first time it's constructed (typically by
`SpringRestServlet.createBeanStore(...)`). This
+means `application.yaml` keys are reachable from a Juneau `@Value`:
+
+```yaml
+# application.yaml
+myapp:
+ title: My Awesome App
+ cache:
+ ttl: PT10M
+```
+
+```java
+@Rest
+public class MyResource extends BasicSpringRestServlet {
+
+ @Value("${myapp.title:Untitled}")
+ private String title;
+
+ @Value("${myapp.cache.ttl:PT5M}")
+ private Duration cacheTtl;
+}
+```
+
+The bridge is removed when `SpringBeanStore.clear()` is called — important for
tests that build
+many transient Spring contexts in the same JVM.
+
+## Classpath-default `Config` (`juneau-config`)
+
+The `juneau-config` module ships a `META-INF/services` entry for
+<a
href="/site/apidocs/org/apache/juneau/config/ConfigPropertySourceProvider.html"
target="_blank">ConfigPropertySourceProvider</a>.
+At startup, `Settings.useServiceLoader()` discovers the provider and registers
a *lazy* bridge to
+`Config.getSystemDefault()`. The resolution is deferred to the first lookup so
it can't trigger
+class-init cycles, and the bridge is silent when no classpath default
`juneau.cfg` exists.
+
+This means a plain `juneau.cfg` on the classpath transparently feeds
`@Value("${section/key}")`
+without any explicit wiring.
+
+## Mutual exclusion with `@Inject`
+
+`@Value` and `@Inject` on the same field, parameter, or setter is an error:
+
+```java
+@Inject
+@Value("${some.key}") // BeanCreationException at injection time
+private String value;
+```
+
+The exception message identifies the offending site to make the conflict easy
to fix.
+
+## See also
+
+- [Simple Variable Language Basics](./SimpleVariableLanguageBasics.md) — the
full SVL surface.
+- [SVL Variables](./SvlVariables.md) — list of built-in `$X{...}` resolvers.
+- [Variable Basics](./VariableBasics.md) — how variables work inside a
`Config`.
+- <a href="/site/apidocs/org/apache/juneau/commons/inject/Value.html"
target="_blank">org.apache.juneau.commons.inject.Value</a>
+- <a href="/site/apidocs/org/apache/juneau/commons/settings/Settings.html"
target="_blank">org.apache.juneau.commons.settings.Settings</a>
+- <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html"
target="_blank">org.apache.juneau.commons.svl.vars.PropertyVar</a>
+- <a
href="/site/apidocs/org/apache/juneau/rest/springboot/SpringEnvironmentPropertySource.html"
target="_blank">org.apache.juneau.rest.springboot.SpringEnvironmentPropertySource</a>
diff --git a/pages/topics/05.04.01.VariableBasics.md
b/pages/topics/05.04.01.VariableBasics.md
index 5b441a3202..06b3d47c0c 100644
--- a/pages/topics/05.04.01.VariableBasics.md
+++ b/pages/topics/05.04.01.VariableBasics.md
@@ -66,3 +66,10 @@ Additionally, the following method can be used to retrieve a
`Config` with a dif
<node-0><java-class><a
href="/site/apidocs/org/apache/juneau/config/Config.html"
target="_blank">Config</a></java-class></node-0>
<node-1><java-method><a
href="/site/apidocs/org/apache/juneau/config/Config.html#resolving(org.apache.juneau.commons.svl.VarResolverSession)"
target="_blank">resolving(VarResolverSession)</a></java-method></node-1>
</tree>
+
+## See also
+
+- [`${xxx}` shortcut](./SimpleVariableLanguageBasics.md) — Spring-style
placeholder syntax that
+ lowers to `$P{...}` (unified property lookup) and supports `${key:default}`
defaulting.
+- [@Value Annotation Basics](./ValueAnnotationBasics.md) — declarative
configuration injection
+ using `@Value("${...}")` on beans, including the Spring Boot
`application.yaml` bridge.
diff --git a/sidebars.ts b/sidebars.ts
index 0a6de7d053..6a2c2111a3 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -347,6 +347,11 @@ const sidebars: SidebarsConfig = {
id:
'topics/02.21.05.SvlOtherNotes',
label:
'2.21.5. SVL Other Notes',
},
+ {
+ type:
'doc',
+ id:
'topics/02.21.06.ValueAnnotationBasics',
+ label:
'2.21.6. @Value Annotation Basics',
+ },
],
},
{