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 527ec08ffe docs(9.5): document BeanInstantiator statics,
CreatableBeanStore removal, WritableBeanStore narrowing, SpringBeanStore
rename; fix BeanStore apidocs link
527ec08ffe is described below
commit 527ec08ffe7b466d86e72f438c36499e04aa2a6c
Author: James Bognar <[email protected]>
AuthorDate: Mon May 11 15:42:32 2026 -0400
docs(9.5): document BeanInstantiator statics, CreatableBeanStore removal,
WritableBeanStore narrowing, SpringBeanStore rename; fix BeanStore apidocs link
Co-authored-by: Cursor <[email protected]>
---
pages/release-notes/9.5.0.md | 124 +++++++++++++++++++++
pages/topics/10.04.03.JavaMethodParameters.md | 2 +-
.../11.01.JuneauRestServerSpringbootBasics.md | 29 +++++
3 files changed, 154 insertions(+), 1 deletion(-)
diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index 6bf004f8ff..a8b76af9fa 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -965,6 +965,91 @@ The following `asX()` methods have been removed in favor
of their `toX()` equiva
| `asReadableString()` *(JsonMap only)* | `toReadableJson5()` |
| `asString(WriterSerializer)` | `toString(WriterSerializer)` |
+#### `BasicBeanStore` precedence-aware affordances
+
+`BasicBeanStore` (in `org.apache.juneau.cp`) gained two new affordances that
power the 9.5 `RestContext` precedence flip (see *juneau-rest-server* below).
Both are opt-in; existing call sites that use only `parent(...)` and local
`addBean(...)` calls keep their current behavior.
+
+- **`Builder.overridingParent(BasicBeanStore)`** — Registers a parent bean
store that is consulted *before* local entries during `getBean(...)` /
`getBeanSupplier(...)`. The classic `parent(...)` slot keeps its post-entry
fallback role. Useful for bridging an outer scope (Spring `ApplicationContext`,
parent-resource bootstrap, test harness) on top of a child bean store and
having the outer scope win without mutating local entries.
+- **`addDefaultSupplier(Class, Supplier)`** (and a named variant) — Registers
a fallback supplier that fires only after local entries and the regular parent
chain have all returned empty. Stored in a separate map from regular entries,
so resolution order is unambiguous and `addBean(...)` calls can override the
default at any time without removing the fallback.
+
+Final resolve order on `BasicBeanStore` is:
+
+```text
+overridingParent.getBeanSupplier(type, name) // outer scope (e.g.
Spring)
+ -> entries[type][name] // addBean(...) /
@RestInject
+ -> parent.getBeanSupplier(type, name) // regular fallback
+ -> defaults[type][name] // addDefaultSupplier(...)
+```
+
+`hasBean(...)` follows the same precedence; a new `hasDefaultSupplier(...)`
accessor lets callers ask "is there a registered default for this type?"
without forcing the supplier to fire.
+
+#### Removal of Legacy `cp` Injection Classes
+
+The following classes in `org.apache.juneau.cp` have been removed as part of
the migration to the `org.apache.juneau.commons.inject` package:
+
+- **`BasicBeanStore`** (was `org.apache.juneau.cp.BasicBeanStore`) — replaced
by `org.apache.juneau.commons.inject.BasicBeanStore` (renamed from
`BasicBeanStore2`).
+- **`BeanCreator`** — replaced by
`org.apache.juneau.commons.inject.BeanInstantiator`.
+- **`BeanBuilder`** — replaced by
`org.apache.juneau.commons.inject.BeanInstantiator` (was the base class for
domain fluent builders; replaced with native builder fields and
`BeanInstantiator`).
+- **`BeanCreateMethodFinder`** — replaced by
`BeanStore.createBeanFromMethod(...)`.
+- **`ContextBeanCreator`** is **not** removed — it solves a different problem
(persistent `Context.Builder` holder for repeated annotation application) and
is retained as a first-class API.
+
+**Migration guide:**
+
+| Legacy (`org.apache.juneau.cp`) | Replacement
(`org.apache.juneau.commons.inject`) |
+|---|---|
+| `BasicBeanStore.create().build()` | `new BasicBeanStore()` |
+| `BasicBeanStore.INSTANCE` | `BasicBeanStore.INSTANCE` |
+| `BasicBeanStore.of(parent)` | `new BasicBeanStore(parent)` |
+| `BeanCreator.of(MyBean.class, store)` | `BeanInstantiator.of(MyBean.class,
store)` |
+| `creator.arg(Type.class, value)` | `instantiator.addBean(Type.class, value)`
|
+| `creator.type(Impl.class)` | `instantiator.type(Impl.class)` |
+| `creator.impl(instance)` | `instantiator.impl(instance)` |
+| `creator.run()` | `instantiator.run()` |
+| `creator.orElse(default)` | `instantiator.asOptional().orElse(default)` |
+| `new BeanCreateMethodFinder(type, obj, bs)` | `bs.createBeanFromMethod(type,
obj, predicate)` |
+
+#### `BeanInstantiator` Static Convenience Methods
+
+Three static factory helpers have been added to `BeanInstantiator` to
eliminate verbose ternary patterns at common call sites:
+
+| Method | Equivalent |
+|---|---|
+| `BeanInstantiator.createOrNull(Class<T>)` | `beanType == null ? null :
BeanInstantiator.of(beanType).fallback(() -> null).run()` |
+| `BeanInstantiator.optionalOf(Class<T>)` | `beanType == null ?
Optional.empty() : BeanInstantiator.of(beanType).asOptional()` |
+| `BeanInstantiator.createOrDefault(Class<T>, T)` | `beanType == null ?
defaultValue : BeanInstantiator.of(beanType).run()` |
+
+All three return `null` / `Optional.empty()` / the default when the class
argument is `null`, avoiding null-checks at every call site. Typical usage:
+
+```java
+// Before:
+var listenerCls = ctx.getListener();
+listener = listenerCls == null ? null :
BeanInstantiator.of(listenerCls).fallback(() -> null).run();
+
+// After:
+listener = BeanInstantiator.createOrNull(ctx.getListener());
+```
+
+#### `CreatableBeanStore` Removed
+
+`CreatableBeanStore` (in `org.apache.juneau.commons.inject`) has been removed.
It was an unused internal interface with no production callers.
`WritableBeanStore` serves as the write-capable interface for the inject
package.
+
+#### `WritableBeanStore` → `BeanStore` in Builder Classes
+
+The following builder-class constructors and fields have been narrowed from
`WritableBeanStore` to `BeanStore` because they only perform read operations
(bean lookup) on the store; they do not add beans:
+
+- `EncoderSet.Builder`
+- `SerializerSet.Builder`
+- `ParserSet.Builder`
+- `ResponseProcessorList.Builder`
+- `RestOpArgList.Builder`
+- `RestMatcherList.Builder`
+- `RestGuardList.Builder`
+- `RestConverterList.Builder`
+- `RestOperations.Builder`
+- `RestChildren.Builder`
+
+Callers that previously passed a `WritableBeanStore` still compile without
changes, since `WritableBeanStore` extends `BeanStore`.
+
### juneau-marshall-rdf
#### Upgraded Apache Jena to 5.6.0
@@ -1076,6 +1161,10 @@ String name
### juneau-rest-server
+#### `SpringBeanStore2` Renamed to `SpringBeanStore`
+
+`SpringBeanStore2` has been renamed to `SpringBeanStore` for consistency with
the `BasicBeanStore2` → `BasicBeanStore` rename completed in this release. The
old name `SpringBeanStore2` is removed; update any import or type reference to
`org.apache.juneau.rest.springboot.SpringBeanStore`.
+
#### `RestServerConstants`
- **`RestServerConstants`** — Central place for **juneau-rest-server** static
literals, starting with `Settings` keys. Serializer/parser session-option
settings (`juneau.rest.sessionOptions.*`) use fields such as
**`SETTING_sessionOptions_rejectWhenAllowlistEmpty`** and
**`SETTING_sessionOptions_failOnInvalidAllowlistEntry`**. HTTP wire names for
those options are fields on **`RestSharedConstants`** in
**juneau-rest-common**. This supersedes the **`RestSessionOptionsSettings`**
type.
@@ -1195,6 +1284,41 @@ Key changes:
See the [V9.5 Migration Guide](/docs/topics/V9.5-migration-guide) for a
per-setting replacement table.
+#### Bean precedence: Spring > `@RestInject` > default (breaking)
+
+The precedence order used by `RestContext` to resolve framework-managed beans
(`CallLogger`, `EncoderSet`, `SerializerSet`, `ParserSet`, `ThrownStore`,
`Config`, `VarResolver`, `HttpPartSerializer`, `HttpPartParser`, `Messages`,
`MethodExecStore`, `JsonSchemaGenerator`, `StaticFiles`, `DebugEnablement`,
`SwaggerProvider`, `RestOperations`, `RestChildren`, named `HeaderList` /
`NamedAttributeMap` slots, etc.) has been flipped:
+
+| Tier | 9.4 and earlier | 9.5+ |
+| ---- | --------------- | ---- |
+| 1 (highest) | `@RestInject` method on the resource | **Spring `@Bean`** (or
any bean reachable through the bootstrap / overriding-parent bean store) |
+| 2 | Spring `@Bean` (via `SpringBeanStore`) | **`@RestInject` method on the
resource** |
+| 3 (lowest) | Memoizer-backed framework default | **Memoizer-backed framework
default** |
+
+`@RestInject` is now documented as a *programmable default*, analogous to
Spring's `@ConditionalOnMissingBean`: if a Spring bean of the same type (and
name, where named) is available, it wins; otherwise the `@RestInject` method
runs and its result is cached. Resolution short-circuits on the first hit, so
non-Spring deployments collapse to the familiar `@RestInject > default` chain.
+
+Mechanically, the change is implemented through three new affordances on
`BasicBeanStore`:
+
+- An **overriding parent** bean store (constructor /
`Builder.overridingParent(...)`) that is consulted *before* local entries
during `getBean(...)` / `getBeanSupplier(...)`. The Spring boot integration
installs the application context bridge here.
+- **`addDefaultSupplier(Class, Supplier)`** (and a named variant) that
registers a fallback supplier consulted *after* local entries and the regular
parent. `RestContext` registers every memoizer-backed framework bean as a
default supplier, so they only fire when nothing higher up in the chain has
bound the type.
+- `RestContext.getBootstrapBeanStore()` continues to return a
`BasicBeanStore`, but the resolution order on that store now reflects the new
precedence model.
+
+Side effects of the new model:
+
+- The legacy `DELAYED_INJECTION` / `DELAYED_INJECTION_NAMES` skip lists in
`RestContext` are gone. The `@RestInject` method walk now runs for every type
with no hand-maintained filter; default-supplier presence is what auto-derives
"skip this for now" behavior.
+- Inside each framework-bean memoizer body, redundant
`bs.getBean(X).ifPresent(impl::override)` lookups have been removed. The bean
store is the precedence engine — defaults no longer probe the store on the way
out.
+- All `RestContext.getX()` accessor methods (`getCallLogger()`, `getConfig()`,
`getVarResolver()`, etc.) now route through `beanStore.getBean(X)`, so internal
callers see Spring overrides without needing to hit the bean store directly.
+
+##### Migration
+
+If you previously relied on `@RestInject` overriding a Spring `@Bean`, you
have a few options in 9.5+:
+
+- **Preferred — let Spring win.** Remove the `@RestInject` method and lean on
the Spring bean. The `@RestInject` was effectively a "default", which Spring
already provides via `@ConditionalOnMissingBean` semantics on the bean factory.
+- **Skip the Spring `@Bean`.** Don't declare the type as a Spring `@Bean` and
the `@RestInject` method will continue to win over the framework default.
+- **Use Spring-native overrides.** Mark the relevant Spring bean with
`@Primary`, `@MockBean`, or `@ConditionalOnProperty` so Spring itself picks the
right candidate. The bean store will then surface that candidate to
`RestContext`.
+- **Programmatic registrations** that flow through
`args.beanStoreConfigurer()` (the `Consumer<BasicBeanStore>` hook on
`RestContextInit`) land as *regular* entries — the same tier as `@RestInject`.
They no longer beat Spring; if you need that, register the bean as a Spring
`@Bean` (or contribute it through the overriding-parent layer in a custom
`BasicBeanStore` subclass).
+
+`SpringBeanStore` keeps backward-compatible behavior at the API level: as a
`BasicBeanStore` subclass it picks up the new precedence model automatically
when used through `RestContext`. Custom `BasicBeanStore` subclasses that want
Spring-equivalent precedence can wire themselves in via
`Builder.overridingParent(...)`; the existing `parent(...)` slot continues to
behave as a regular fallback (consulted after local entries).
+
#### `RestResponse.setSerializer(Serializer)`
- **`RestResponse.setSerializer(Serializer)`** — Forces the serializer used
for the response body (via `setContent(Object)` or equivalent), bypassing
`Accept` header negotiation. Pass `null` to clear the override. `Content-Type`
is still set by the response processor from the serializer when appropriate;
you can call `setContentType(String)` first if needed.
diff --git a/pages/topics/10.04.03.JavaMethodParameters.md
b/pages/topics/10.04.03.JavaMethodParameters.md
index 5f23278d3d..da9bf1e4c0 100644
--- a/pages/topics/10.04.03.JavaMethodParameters.md
+++ b/pages/topics/10.04.03.JavaMethodParameters.md
@@ -12,7 +12,7 @@ Java methods can contain any of the following parameters in
any order:
<node-1>**Response objects:**</node-1>
<node-2><javac-class>`HttpServletResponse`</javac-class> <javac-class><a
href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/OutputStream.html"
target="_blank">OutputStream</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/RestResponse.html"
target="_blank">RestResponse</a></javac-class>
<javac-class>`ServletOutputStream`</javac-class> <javac-class><a
href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Writer.html"
[...]
<node-1>**Session objects:**</node-1>
-<node-2><javac-class>`HttpSession`</javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/RestSession.html"
target="_blank">RestSession</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/util/UrlPath.html"
target="_blank">UrlPath</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/util/UrlPathMatch.html"
target="_blank">UrlPathMatch</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/cp/BeanStore.htm [...]
+<node-2><javac-class>`HttpSession`</javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/RestSession.html"
target="_blank">RestSession</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/util/UrlPath.html"
target="_blank">UrlPath</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/rest/util/UrlPathMatch.html"
target="_blank">UrlPathMatch</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/commons/inject/B [...]
<node-1>**Parsed request header values:**</node-1>
<node-2><javac-class><a
href="/site/apidocs/org/apache/juneau/http/header/Accept.html"
target="_blank">Accept</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/http/header/AcceptCharset.html"
target="_blank">AcceptCharset</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/http/header/AcceptEncoding.html"
target="_blank">AcceptEncoding</a></javac-class> <javac-class><a
href="/site/apidocs/org/apache/juneau/http/header/AcceptLanguage.html" target=
[...]
<node-1>**Context values:**</node-1>
diff --git a/pages/topics/11.01.JuneauRestServerSpringbootBasics.md
b/pages/topics/11.01.JuneauRestServerSpringbootBasics.md
index fc3f0d8404..6f45086694 100644
--- a/pages/topics/11.01.JuneauRestServerSpringbootBasics.md
+++ b/pages/topics/11.01.JuneauRestServerSpringbootBasics.md
@@ -29,3 +29,32 @@ org.apache.juneau.rest.server.springboot_0.0.0.jar
The `juneau-rest-server-springboot` library provides classes to make it easy
to integrate Juneau REST resources with
Spring and Spring Boot.
+
+#### Bean Precedence (since 9.5.0)
+
+When a Juneau REST resource runs inside a Spring Boot application,
framework-managed beans on `RestContext`
+(`CallLogger`, `EncoderSet`, `SerializerSet`, `ParserSet`, `ThrownStore`,
`Config`, `VarResolver`,
+`HttpPartSerializer`, `HttpPartParser`, etc.) are resolved in the following
order — the first match wins:
+
+1. **Spring `@Bean`** — any bean of the matching type (and name, where
applicable) reachable through the
+ Spring `ApplicationContext` via `SpringBeanStore`.
+2. **`@RestInject` method/field on the resource class** — treated as a
*programmable default*, analogous to
+ Spring's `@ConditionalOnMissingBean`.
+3. **Memoizer-backed framework default** — Juneau's built-in implementation
(`BasicCallLogger`,
+ `BasicEncoderSet`, etc.).
+
+This is a behavior change from 9.4 and earlier, where `@RestInject` won over
Spring. Migration tips:
+
+- **Preferred — let Spring win.** Remove the `@RestInject` method and lean on
the Spring bean. The
+ `@RestInject` was effectively a "default", which Spring already provides
through the bean factory.
+- **Skip the Spring `@Bean`.** Don't expose the type as a Spring `@Bean` and
the `@RestInject` method on
+ the resource will continue to win over the framework default.
+- **Use Spring-native overrides.** Mark the relevant Spring bean with
`@Primary`, `@MockBean`, or a
+ `@ConditionalOn...` predicate so Spring itself picks the right candidate;
that candidate then becomes the
+ one Juneau sees through `SpringBeanStore`.
+
+Internally, this is implemented by installing `SpringBeanStore` as the
**overriding parent** of the
+`RestContext`'s bean store (via
`BasicBeanStore.Builder.overridingParent(...)`), and by registering every
+framework default as a `addDefaultSupplier(...)` entry on the same store — so
Spring beans are consulted
+before any local registration, and built-in defaults fire only when nothing
higher up has bound the type.
+See the 9.5.0 release notes for the full per-tier breakdown.