This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 9020569472 style: reformat simple memoizer fields to multi-line 
Javadoc + lambda style in RestContext
9020569472 is described below

commit 9020569472b3f1291f7b0a71ce4fb02149e2ae81
Author: James Bognar <[email protected]>
AuthorDate: Thu May 7 08:02:38 2026 -0400

    style: reformat simple memoizer fields to multi-line Javadoc + lambda style 
in RestContext
---
 .../java/org/apache/juneau/rest/RestContext.java   | 460 +++++++++------------
 .../org/apache/juneau/rest/annotation/Rest.java    |   1 -
 2 files changed, 193 insertions(+), 268 deletions(-)

diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index dee498cc9e..58d0f7f9c4 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -63,7 +63,6 @@ import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.httppart.*;
-import org.apache.juneau.httppart.HttpPartSerializer.*;
 import org.apache.juneau.jsonschema.*;
 import org.apache.juneau.oapi.*;
 import org.apache.juneau.parser.*;
@@ -295,106 +294,27 @@ public class RestContext extends Context {
                                .anyMatch(x -> nn(x) && x.methodScope().length 
== 0 && (n(name) || eq(x.name(), name)));
                }
 
-               private BasicBeanStore beanStore;
-               private BasicBeanStore bootstrapBeanStore;
                private final Class<?> resourceClass;
-               private Config config;
                private final RestContext parentContext;
-               private RestOpArgList.Builder restOpArgs;
-               private ResponseProcessorList.Builder responseProcessors;
-               private ResourceSupplier resource;
                private final ServletConfig inner;
-               private VarResolver bootstrapVarResolver;
+               final Args args;
 
                /**
                 * Package-private constructor.
                 *
                 * <p>
-                * Demoted from {@code protected} to package-private in the 
April 2026 refactor (2026-04-19). Only
-                * {@link RestContext#RestContext(Args)} instantiates this type 
now.
+                * Minimized in the May 2026 refactor — the beanStore setup, 
{@code @RestInject} processing,
+                * {@code @RestInit} hooks, and {@code beanStoreConfigurer} 
call were all moved into
+                * {@link RestContext#RestContext(Builder)}. The Builder now 
only stores the three fields
+                * that are final and needed by the {@link ServletConfig} 
overrides and factory methods.
                 *
                 * @param rci The bootstrap arguments. Must not be {@code null}.
-                * @throws ServletException If hook method calls failed.
                 */
-               Builder(Args rci) throws ServletException {
-
+               Builder(Args rci) {
                        this.resourceClass = rci.resourceClass();
                        this.inner = rci.servletConfig();
                        this.parentContext = rci.parentContext();
-
-                       if (nn(parentContext))
-                               bootstrapBeanStore = 
parentContext.bootstrapBeanStore;
-
-                       this.resource = new ResourceSupplier(resourceClass, 
assertArgNotNull("resource", rci.resource()));
-                       var r = this.resource;
-                       var rc = resourceClass;
-
-                       // @formatter:off
-                       // Note: pre-9.5 this also called 
.addBean(Builder.class, this) so user code could request the
-                       // in-flight RestContext.Builder via @RestInit method 
parameters or @RestInject method-finder
-                       // resolution. That injection protocol was deleted in 
the April 2026 refactor (zero non-test callers
-                       // across the entire codebase) — the Builder is now a 
private staging detail of RestContext
-                       // construction, not a user-visible bean.
-                       beanStore = createBeanStore(rci.resource())
-                               .build()
-                               .addBean(ResourceSupplier.class, this.resource)
-                               .addBean(ServletConfig.class, nn(inner) ? inner 
: this)
-                               .addBean(ServletContext.class, (nn(inner) ? 
inner : this).getServletContext());
-                       // @formatter:on
-
-                       if (bootstrapBeanStore == null) {
-                               bootstrapBeanStore = beanStore;
-                               beanStore = 
BasicBeanStore.of(bootstrapBeanStore);
-                       }
-                       var bs = beanStore;
-
-                       beanStore.add(BasicBeanStore.class, bs);
-                       beanStore.add(VarResolver.class, 
bootstrapVarResolver());
-                       config = beanStore.add(Config.class, createConfig(bs, 
r, rc));
-
-                       var rci2 = ClassInfo.of(resourceClass);
-
-                       // Get @RestInject fields initialized with values.
-                       // @formatter:off
-                       rci2.getAllFields().stream()
-                               .filter(x -> x.hasAnnotation(RestInject.class))
-                               .forEach(x -> 
opt(x.get(rci.resource().get())).ifPresent(
-                                       y -> beanStore.add(
-                                               x.getFieldType().inner(),
-                                               y,
-                                               
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null))
-                                       )
-                               ));
-                       // @formatter:on
-
-                       rci2.getAllMethods().stream().filter(x -> 
x.hasAnnotation(RestInject.class)).forEach(x -> {
-                               var rt = x.getReturnType().<Object>inner();
-                               var name = 
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null));
-                               if (! (DELAYED_INJECTION.contains(rt) || 
DELAYED_INJECTION_NAMES.contains(name))) {
-                                       // @formatter:off
-                                       new BeanCreateMethodFinder<>(rt, 
rci.resource().get(), beanStore)
-                                               
.find(Builder::isRestInjectMethod)
-                                               .run(y -> beanStore.add(rt, y, 
name));
-                                       // @formatter:on
-                               }
-                       });
-
-                       runInitHooks(bs, resource());
-
-                       // Set @RestInject fields not initialized with values.
-                       // @formatter:off
-                       rci2.getAllFields().stream()
-                               .filter(x -> x.hasAnnotation(RestInject.class))
-                               .forEach(x -> x.setIfNull(
-                                       rci.resource().get(),
-                                       beanStore.getBean(
-                                               x.getFieldType().inner(),
-                                               
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null))
-                                       ).orElse(null)
-                               ));
-                       // @formatter:on
-
-                       rci.beanStoreConfigurer().accept(beanStore());
+                       this.args = rci;
                }
 
                @Override /* Context.Builder is abstract - copy() is not 
meaningful for the transient RestContext bootstrap state. */
@@ -402,69 +322,6 @@ public class RestContext extends Context {
                        throw new NoSuchMethodError("Not implemented.");
                }
 
-               /**
-                * Returns the bean store in this builder.
-                *
-                * <p>
-                * The bean store is a simple storage database for beans keyed 
by type and name.
-                *
-                * <p>
-                * The bean store is created with the parent root bean store as 
the parent, allowing any beans in the root bean store to be available
-                * in this builder.  The root bean store typically pulls from 
an injection framework such as Spring to allow injected beans to be used.
-                *
-                * <p>
-                * The default bean store can be overridden via any of the 
following:
-                * <ul class='spaced-list'>
-                *      <li>Class annotation:  {@link Rest#beanStore() 
@Rest(beanStore)}
-                *      <li>{@link RestInject @RestInject}-annotated methods:
-                *              <p class='bjava'>
-                *      <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] 
BasicBeanStore myMethod(<i>&lt;args&gt;</i>) {...}
-                *              </p>
-                *              Args can be any injected bean including {@link 
org.apache.juneau.cp.BasicBeanStore.Builder}, the default builder.
-                * </ul>
-                *
-                * @return The bean store in this builder.
-                */
-               public BasicBeanStore beanStore() {
-                       return beanStore;
-               }
-
-               /**
-                * Returns the external configuration file for this resource.
-                *
-                * <p>
-                * The config file contains arbitrary configuration information 
that can be accessed by this class, usually
-                * via <c>$C</c> variables.
-                *
-                * <p>
-                * The default config can be overridden via any of the 
following:
-                * <ul class='spaced-list'>
-                *      <li>Injected via bean store.
-                *      <li>Class annotation:  {@link Rest#config() 
@Rest(config)}
-                *      <li>{@link RestInject @RestInject}-annotated method:
-                *              <p class='bjava'>
-                *      <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] 
Config myMethod(<i>&lt;args&gt;</i>) {...}
-                *              </p>
-                *              Args can be any injected bean.
-                * </ul>
-                *
-                * <p>
-                * If a config file is not set up, then an empty config file 
will be returned that is not backed by any file.
-                *
-                * <p>
-                * This bean can be accessed directly via {@link 
RestContext#getConfig()} or passed in as a parameter
-                * on a {@link RestOp}-annotated method.
-                *
-                * <h5 class='section'>See Also:</h5><ul>
-                *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/ConfigurationFiles";>Configuration 
Files</a>
-                * </ul>
-                *
-                * @return The external configuration file for this resource.
-                */
-               public Config config() {
-                       return config;
-               }
-
                @Override /* Overridden from ServletConfig */
                public String getInitParameter(String name) {
                        return inner == null ? null : 
inner.getInitParameter(name);
@@ -487,18 +344,6 @@ public class RestContext extends Context {
                @Override /* Overridden from ServletConfig */
                public String getServletName() { return inner == null ? null : 
inner.getServletName(); }
 
-               /**
-                * Returns the REST servlet/bean instance that this context is 
defined against.
-                *
-                * @return The REST servlet/bean instance that this context is 
defined against.
-                */
-               @SuppressWarnings({
-                       "java:S1452"  // Wildcard required - Supplier<?> for 
generic REST resource instance
-               })
-               public Supplier<?> resource() {
-                       return Objects.requireNonNull(resource, "Resource not 
available. init(Object) has not been called.");
-               }
-
                /**
                 * Returns the response processor list sub-builder.
                 *
@@ -588,63 +433,19 @@ public class RestContext extends Context {
                 *
                 * @return The response processor list sub-builder.
                 */
-               public ResponseProcessorList.Builder responseProcessors() {
-                       if (responseProcessors == null)
-                               responseProcessors = 
createResponseProcessors(beanStore(), resource());
-                       return responseProcessors;
+               public ResponseProcessorList.Builder 
responseProcessors(BasicBeanStore beanStore, Supplier<?> resource) {
+                       return createResponseProcessors(beanStore, resource);
                }
 
                /**
                 * Returns the REST operation args sub-builder.
                 *
+                * @param beanStore The bean store.
+                * @param resource The resource supplier.
                 * @return The REST operation args sub-builder.
                 */
-               public RestOpArgList.Builder restOpArgs() {
-                       if (restOpArgs == null)
-                               restOpArgs = createRestOpArgs(beanStore(), 
resource());
-                       return restOpArgs;
-               }
-
-               /**
-                * Returns the bootstrap bean store.
-                *
-                * <p>
-                * This is the bean store inherited from the parent resource 
and does not include
-                * any beans added by this class.
-                *
-                * @return The bootstrap bean store.
-                */
-               public BasicBeanStore bootstrapBeanStore() {
-                       return bootstrapBeanStore;
-               }
-
-               /**
-                * Returns the bootstrap (pre-runtime) variable resolver for 
this REST context.
-                *
-                * <p>
-                * The bootstrap resolver is used during context construction 
to resolve SVL variables in annotation attribute values
-                * (e.g. <c>@Rest(messages=...)</c>, <c>@Rest(config=...)</c>) 
before the runtime {@link VarResolver} — which has
-                * {@link Messages} and {@link Config} beans wired in — is 
available. It exposes the same {@link Var} catalog as
-                * the runtime resolver, but {@link LocalizationVar} and {@link 
ConfigVar} resolve to empty strings because their
-                * backing beans haven't been built yet.
-                *
-                * <p>
-                * To override the bootstrap resolver, declare a named {@link 
RestInject @RestInject} static method on the resource
-                * class:
-                * <p class='bjava'>
-                *      
<ja>@RestInject</ja>(name=<js>"bootstrapVarResolver"</js>) <jk>public 
static</jk> VarResolver myBootstrapResolver(<i>&lt;args&gt;</i>) {...}
-                * </p>
-                *
-                * <h5 class='section'>See Also:</h5><ul>
-                *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/RestServerSvlVariables";>SVL 
Variables</a>
-                * </ul>
-                *
-                * @return The bootstrap (pre-runtime) variable resolver. 
Cached on first call.
-                */
-               public VarResolver bootstrapVarResolver() {
-                       if (bootstrapVarResolver == null)
-                               bootstrapVarResolver = 
createBootstrapVarResolver(beanStore, resource, resourceClass);
-                       return bootstrapVarResolver;
+               public RestOpArgList.Builder restOpArgs(BasicBeanStore 
beanStore, Supplier<?> resource) {
+                       return createRestOpArgs(beanStore, resource);
                }
 
                private static void runInitHooks(BasicBeanStore beanStore, 
Supplier<?> resource) throws ServletException {
@@ -704,18 +505,20 @@ public class RestContext extends Context {
                /**
                 * Creates the bean store in this builder.
                 *
+                * @param parentBs
+                *      The parent (bootstrap) bean store to layer onto, or 
{@code null} for root resources.
                 * @param resource
                 *      The REST servlet/bean instance that this context is 
defined against.
                 * @return A new bean store builder.
                 */
-               protected BasicBeanStore.Builder createBeanStore(Supplier<?> 
resource) {
+               protected BasicBeanStore.Builder createBeanStore(BasicBeanStore 
parentBs, Supplier<?> resource) {
 
                        // Default value.
                        // @formatter:off
                        var v = Value.of(
                                BasicBeanStore
                                        .create()
-                                       .parent(bootstrapBeanStore())
+                                       .parent(parentBs)
                                );
                        // @formatter:on
 
@@ -738,7 +541,6 @@ public class RestContext extends Context {
                 *
                 * <h5 class='section'>See Also:</h5><ul>
                 *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/ConfigurationFiles";>Configuration 
Files</a>
-                *      <li class='jm'>{@link #config()}
                 * </ul>
                 *
                 * @param beanStore
@@ -1299,6 +1101,9 @@ public class RestContext extends Context {
        private HttpPartParser.Creator partParserCreator;
        private HttpPartSerializer.Creator partSerializerCreator;
        private JsonSchemaGenerator.Builder jsonSchemaGeneratorBuilder;
+       private VarResolver rawBootstrapVarResolver;
+       private Config rawConfig;
+       private AnnotationWorkList annotationWork;
 
        // Private accessors used by memoizer lambdas to satisfy Java's 
definite-assignment rules for blank final fields.
        private BasicBeanStore beanStore() { return beanStore; }
@@ -1335,7 +1140,7 @@ public class RestContext extends Context {
         * Cached on the builder so that {@code init()} and the {@link 
#messages} / {@link #config}
         * memoizers all see the same instance during construction.
         */
-       private final Memoizer<VarResolver> bootstrapVarResolver = memoizer(() 
-> builder().bootstrapVarResolver());
+       private final Memoizer<VarResolver> bootstrapVarResolver = memoizer(() 
-> rawBootstrapVarResolver);
 
        /**
         * The {@link CallLogger} for this resource.
@@ -1369,7 +1174,7 @@ public class RestContext extends Context {
         * Wraps the builder's unresolved config with a {@link VarResolver} 
session so that SVL variables
         * in config values are expanded on access.
         */
-       private final Memoizer<Config> config = memoizer(() -> 
builder().config().resolving(getVarResolver().createSession()));
+       private final Memoizer<Config> config = memoizer(() -> 
rawConfig.resolving(getVarResolver().createSession()));
 
        /**
         * The supported request content types ({@code Content-Type} media 
types) for this resource.
@@ -1416,7 +1221,7 @@ public class RestContext extends Context {
                if (nn(resolvedDebugDefault))
                        bs.addBean(Enablement.class, resolvedDebugDefault);
                else if (bs.getBean(Enablement.class).isEmpty())
-                       bs.addBean(Enablement.class, builder().isDebug() ? 
Enablement.ALWAYS : Enablement.NEVER);
+                       bs.addBean(Enablement.class, isDebug() ? 
Enablement.ALWAYS : Enablement.NEVER);
                var creator = BeanCreator.of(DebugEnablement.class, 
bs).type(BasicDebugEnablement.class);
                // Order matters — annotations override defaults so they need 
to be applied last.
                bs.getBeanType(DebugEnablement.class).ifPresent(creator::type);
@@ -1441,9 +1246,7 @@ public class RestContext extends Context {
         */
        private final Memoizer<NamedAttributeMap> defaultRequestAttributes = 
memoizer(() -> {
                var v = Value.of(NamedAttributeMap.create());
-               var anns = new ArrayList<>(getRestAnnotations());
-               Collections.reverse(anns);
-               anns.forEach(ai -> 
Arrays.stream(ai.inner().defaultRequestAttributes())
+               getRestAnnotationsTopDown().forEach(ai -> 
Arrays.stream(ai.inner().defaultRequestAttributes())
                        .filter(StringUtils::isNotBlank)
                        .map(this::resolve)
                        .filter(StringUtils::isNotBlank)
@@ -1464,9 +1267,7 @@ public class RestContext extends Context {
         */
        private final Memoizer<HeaderList> defaultRequestHeaders = memoizer(() 
-> {
                var v = Value.of(HeaderList.create());
-               var anns = new ArrayList<>(getRestAnnotations());
-               Collections.reverse(anns);
-               anns.forEach(ai -> {
+               getRestAnnotationsTopDown().forEach(ai -> {
                        Rest a = ai.inner();
                        
Arrays.stream(a.defaultRequestHeaders()).filter(StringUtils::isNotBlank).map(this::resolve).filter(StringUtils::isNotBlank).map(s
 -> stringHeader(s)).forEach(v.get()::setDefault);
                        var defaultAccept = resolve(a.defaultAccept());
@@ -1490,9 +1291,7 @@ public class RestContext extends Context {
         */
        private final Memoizer<HeaderList> defaultResponseHeaders = memoizer(() 
-> {
                var v = Value.of(HeaderList.create());
-               var anns = new ArrayList<>(getRestAnnotations());
-               Collections.reverse(anns);
-               anns.forEach(ai -> 
Arrays.stream(ai.inner().defaultResponseHeaders()).filter(StringUtils::isNotBlank).map(this::resolve).filter(StringUtils::isNotBlank).map(s
 -> stringHeader(s)).forEach(v.get()::setDefault));
+               getRestAnnotationsTopDown().forEach(ai -> 
Arrays.stream(ai.inner().defaultResponseHeaders()).filter(StringUtils::isNotBlank).map(this::resolve).filter(StringUtils::isNotBlank).map(s
 -> stringHeader(s)).forEach(v.get()::setDefault));
                beanStore().getBean(HeaderList.class, 
PROP_defaultResponseHeaders).ifPresent(v::set);
                new BeanCreateMethodFinder<>(HeaderList.class, 
resource().get(), beanStore()).addBean(HeaderList.class, v.get()).find(x -> 
Builder.isRestInjectMethod(x, PROP_defaultResponseHeaders)).run(v::set);
                return v.get();
@@ -1563,12 +1362,10 @@ public class RestContext extends Context {
        private final Memoizer<Messages> messages = memoizer(() -> {
                var b = Messages.create(resourceClass());
                // Walk @Rest annotations parent-to-child; child wins because 
location() prepends.
-               var anns = new ArrayList<>(getRestAnnotations());
-               Collections.reverse(anns);
                // Resolve location strings against the simple resolver — full 
resolver isn't available yet
                // (it depends on getMessages()).
                var vrs = getBootstrapVarResolver().createSession();
-               anns.forEach(ai -> 
ai.getString(PROPERTY_messages).filter(StringUtils::isNotBlank).ifPresent(s -> 
b.location(vrs.resolve(s))));
+               getRestAnnotationsTopDown().forEach(ai -> 
ai.getString(PROPERTY_messages).filter(StringUtils::isNotBlank).ifPresent(s -> 
b.location(vrs.resolve(s))));
                beanStore().getBean(Messages.class).ifPresent(b::impl);
                new BeanCreateMethodFinder<>(Messages.class, resource().get(), 
beanStore()).addBean(Messages.Builder.class, 
b).find(Builder::isRestInjectMethod).run(b::impl);
                return b.build();
@@ -1842,7 +1639,7 @@ public class RestContext extends Context {
                var bs = beanStore();
                var b = getBootstrapVarResolver().copy()
                        .bean(Messages.class, getMessages())
-                       .bean(Config.class, builder().config());
+                       .bean(Config.class, rawConfig);
                bs.getBean(VarResolver.class).ifPresent(b::impl);
                new BeanCreateMethodFinder<>(VarResolver.class, 
resource().get(), bs).addBean(VarResolver.Builder.class, 
b).find(Builder::isRestInjectMethod).run(b::impl);
                return b.build();
@@ -1953,21 +1750,87 @@ public class RestContext extends Context {
                        this.builder = builder;
 
                        parentContext = builder.parentContext;
-                       resource = builder.resource;
                        resourceClass = builder.resourceClass;
-                       bootstrapBeanStore = builder.bootstrapBeanStore();
+                       var rs = new ResourceSupplier(resourceClass, 
assertArgNotNull("resource", builder.args.resource()));
+                       resource = rs;
 
-                       BasicBeanStore bs = beanStore = builder.beanStore();
+                       // --- beanStore setup (moved from Builder constructor, 
May 2026 refactor) ---
+
+                       // Determine the parent (bootstrap) store: inherited 
from parent resource if present.
+                       BasicBeanStore parentBs = parentContext != null ? 
parentContext.bootstrapBeanStore : null;
+
+                       // Build the initial beanStore; apply @Rest(beanStore) 
+ optional @RestInject override.
                        // @formatter:off
-                       beanStore
-                               .addBean(BasicBeanStore.class, beanStore)
-                               .addBean(RestContext.class, this)
-                               .addBean(Object.class, resource.get())
-                               .addBean(Builder.class, builder)
-                               .addBean(AnnotationWorkList.class, 
builder.getApplied());
+                       BasicBeanStore bs = builder.createBeanStore(parentBs, 
rs)
+                               .build()
+                               .addBean(ResourceSupplier.class, rs)
+                               .addBean(ServletConfig.class, nn(builder.inner) 
? builder.inner : builder)
+                               .addBean(ServletContext.class, 
(nn(builder.inner) ? builder.inner : builder).getServletContext());
+                       // @formatter:on
+
+                       // If no parent store, promote bs to bootstrap and 
layer a fresh child store on top.
+                       if (parentBs == null) {
+                               bootstrapBeanStore = bs;
+                               bs = BasicBeanStore.of(bootstrapBeanStore);
+                       } else {
+                               bootstrapBeanStore = parentBs;
+                       }
+                       beanStore = bs;
+
+                       beanStore.add(BasicBeanStore.class, beanStore);
+                       rawBootstrapVarResolver = 
builder.createBootstrapVarResolver(beanStore, resource, resourceClass);
+                       beanStore.add(VarResolver.class, 
rawBootstrapVarResolver);
+                       rawConfig = builder.createConfig(beanStore, resource, 
resourceClass);
+                       beanStore.add(Config.class, rawConfig);
+
+                       var rci2 = ClassInfo.of(resourceClass);
+
+                       // Register @RestInject fields that already have a 
value.
+                       // @formatter:off
+                       rci2.getAllFields().stream()
+                               .filter(x -> x.hasAnnotation(RestInject.class))
+                               .forEach(x -> 
opt(x.get(resource.get())).ifPresent(
+                                       y -> beanStore.add(
+                                               x.getFieldType().inner(),
+                                               y,
+                                               
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null))
+                                       )
+                               ));
+                       // @formatter:on
+
+                       // Run non-delayed @RestInject methods.
+                       rci2.getAllMethods().stream().filter(x -> 
x.hasAnnotation(RestInject.class)).forEach(x -> {
+                               var rt = x.getReturnType().<Object>inner();
+                               var name = 
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null));
+                               if (! (Builder.DELAYED_INJECTION.contains(rt) 
|| Builder.DELAYED_INJECTION_NAMES.contains(name))) {
+                                       // @formatter:off
+                                       new BeanCreateMethodFinder<>(rt, 
resource.get(), beanStore)
+                                               
.find(Builder::isRestInjectMethod)
+                                               .run(y -> beanStore.add(rt, y, 
name));
+                                       // @formatter:on
+                               }
+                       });
+
+                       Builder.runInitHooks(beanStore, resource);
+
+                       // Back-fill @RestInject fields that were null before 
init hooks ran.
+                       // @formatter:off
+                       rci2.getAllFields().stream()
+                               .filter(x -> x.hasAnnotation(RestInject.class))
+                               .forEach(x -> x.setIfNull(
+                                       resource.get(),
+                                       beanStore.getBean(
+                                               x.getFieldType().inner(),
+                                               
RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null))
+                                       ).orElse(null)
+                               ));
                        // @formatter:on
 
-                               // Path is read directly from the @Rest(path) 
annotation chain (most-derived class wins),
+                       builder.args.beanStoreConfigurer().accept(beanStore);
+
+                       // --- end beanStore setup ---
+
+                       // Path is read directly from the @Rest(path) 
annotation chain (most-derived class wins),
                        // replacing the prior Builder.path staging field 
eliminated in the May 2026 refactor.
                        path = getRestAnnotations().stream()
                                .map(ai -> ai.inner().path())
@@ -1981,19 +1844,25 @@ public class RestContext extends Context {
                                p += "/*";
                        pathMatcher = UrlPathMatcher.of(p);
 
-                       // Initialize sub-builders and apply @Rest annotation 
work (moved here from Builder constructor,
-                       // May 2026 refactor — sub-builder fields now live on 
RestContext rather than Builder).
-                       beanContextBuilder = builder.createBeanContext(bs, 
resource);
-                       partParserCreator = builder.createPartParser(bs, 
resource);
-                       partSerializerCreator = 
builder.createPartSerializer(bs, resource);
-                       jsonSchemaGeneratorBuilder = 
builder.createJsonSchemaGenerator(bs, resource);
-                       var rci2 = ClassInfo.of(resourceClass);
-                       var vrs = 
builder.bootstrapVarResolver().createSession();
-                       var work = AnnotationWorkList.of(vrs, 
rstream(AnnotationProvider.INSTANCE.find(rci2)).filter(CONTEXT_APPLY_FILTER));
-                       beanContextBuilder.apply(work);
-                       partSerializerCreator.apply(work);
-                       partParserCreator.apply(work);
-                       jsonSchemaGeneratorBuilder.apply(work);
+                       // Initialize sub-builders and apply @Rest annotation 
work.
+                       beanContextBuilder = 
builder.createBeanContext(beanStore, resource);
+                       partParserCreator = builder.createPartParser(beanStore, 
resource);
+                       partSerializerCreator = 
builder.createPartSerializer(beanStore, resource);
+                       jsonSchemaGeneratorBuilder = 
builder.createJsonSchemaGenerator(beanStore, resource);
+                       var vrs = rawBootstrapVarResolver.createSession();
+                       annotationWork = AnnotationWorkList.of(vrs, 
rstream(AnnotationProvider.INSTANCE.find(rci2)).filter(CONTEXT_APPLY_FILTER));
+                       beanContextBuilder.apply(annotationWork);
+                       partSerializerCreator.apply(annotationWork);
+                       partParserCreator.apply(annotationWork);
+                       jsonSchemaGeneratorBuilder.apply(annotationWork);
+
+                       // @formatter:off
+                       beanStore
+                               .addBean(RestContext.class, this)
+                               .addBean(Object.class, resource.get())
+                               .addBean(Builder.class, builder)
+                               .addBean(AnnotationWorkList.class, 
annotationWork);
+                       // @formatter:on
 
                        bs.addBean(BeanContext.class, getBeanContext());
                        bs.add(EncoderSet.class, getEncoders());
@@ -2123,6 +1992,20 @@ public class RestContext extends Context {
                return Collections.unmodifiableList(combined);
        });
 
+       /**
+        * The {@code @Rest} annotation list for this resource in 
parent-to-child (top-down) order.
+        *
+        * <p>
+        * This is the reverse of {@link #restAnnotations}: most-ancestor 
annotation first, most-derived last.
+        * Use this when walking the annotation chain in inheritance order so 
that child values append after or
+        * override parent values.
+        */
+       private final Memoizer<List<AnnotationInfo<Rest>>> 
restAnnotationsTopDown = memoizer(() -> {
+               var list = new ArrayList<>(getRestAnnotations());
+               Collections.reverse(list);
+               return Collections.unmodifiableList(list);
+       });
+
        private Stream<AnnotationInfo<Rest>> 
restAnnotationsForPropertySortedByRank(String propertyName) {
                return 
getRestAnnotationsForProperty(propertyName).sorted(Comparator.comparingInt(AnnotationInfo::getRank));
        }
@@ -2170,18 +2053,47 @@ public class RestContext extends Context {
                return v.get();
        }
 
-       /** Header names that may be passed via URL query parameter; resolved 
from {@code @Rest(allowedHeaderParams)}, default {@code "Accept,Content-Type"}. 
*/
-       private final Memoizer<Set<String>> allowedHeaderParams      = 
memoizer(() -> 
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedHeaderParams,
 env("RestContext.allowedHeaderParams", "Accept,Content-Type")))));
-       /** HTTP method names that may be specified via a request header; 
resolved from {@code @Rest(allowedMethodHeaders)}, default empty. */
-       private final Memoizer<Set<String>> allowedMethodHeaders     = 
memoizer(() -> 
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodHeaders,
 env("RestContext.allowedMethodHeaders").orElse("")))));
-       /** HTTP method names that may be specified via URL query parameter; 
resolved from {@code @Rest(allowedMethodParams)}, default {@code 
"HEAD,OPTIONS"}. */
-       private final Memoizer<Set<String>> allowedMethodParams      = 
memoizer(() -> 
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodParams,
 env("RestContext.allowedMethodParams", "HEAD,OPTIONS")))));
-       /** Whether a {@code &content=} URL parameter may override the request 
body; inverse of {@code @Rest(disableContentParam)}. */
-       private final Memoizer<Boolean>     allowContentParam        = 
memoizer(() -> !mergeReplacedBooleanAttribute(PROPERTY_disableContentParam, 
env("RestContext.disableContentParam", false)));
-       /** Whether exception stack traces are rendered in error responses; 
resolved from {@code @Rest(renderResponseStackTraces)}. */
-       private final Memoizer<Boolean>     renderResponseStackTraces = 
memoizer(() -> 
mergeReplacedBooleanAttribute(PROPERTY_renderResponseStackTraces, 
env("RestContext.renderResponseStackTraces", false)));
-       /** The request header used for client-version matching; resolved from 
{@code @Rest(clientVersionHeader)}, default {@code "Client-Version"}. */
-       private final Memoizer<String>      clientVersionHeader      = 
memoizer(() -> mergeReplacedStringAttribute(PROPERTY_clientVersionHeader, 
env("RestContext.clientVersionHeader", "Client-Version")));
+       /**
+        * Header names that may be passed via URL query parameter; resolved 
from {@code @Rest(allowedHeaderParams)},
+        * default {@code "Accept,Content-Type"}.
+        */
+       private final Memoizer<Set<String>> allowedHeaderParams = memoizer(() ->
+               
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedHeaderParams,
 env("RestContext.allowedHeaderParams", "Accept,Content-Type")))));
+
+       /**
+        * HTTP method names that may be specified via a request header; 
resolved from {@code @Rest(allowedMethodHeaders)},
+        * default empty.
+        */
+       private final Memoizer<Set<String>> allowedMethodHeaders = memoizer(() 
->
+               
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodHeaders,
 env("RestContext.allowedMethodHeaders").orElse("")))));
+
+       /**
+        * HTTP method names that may be specified via URL query parameter; 
resolved from {@code @Rest(allowedMethodParams)},
+        * default {@code "HEAD,OPTIONS"}.
+        */
+       private final Memoizer<Set<String>> allowedMethodParams = memoizer(() ->
+               
Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodParams,
 env("RestContext.allowedMethodParams", "HEAD,OPTIONS")))));
+
+       /**
+        * Whether a {@code &content=} URL parameter may override the request 
body; inverse of
+        * {@code @Rest(disableContentParam)}.
+        */
+       private final Memoizer<Boolean> allowContentParam = memoizer(() ->
+               !mergeReplacedBooleanAttribute(PROPERTY_disableContentParam, 
env("RestContext.disableContentParam", false)));
+
+       /**
+        * Whether exception stack traces are rendered in error responses; 
resolved from
+        * {@code @Rest(renderResponseStackTraces)}.
+        */
+       private final Memoizer<Boolean> renderResponseStackTraces = memoizer(() 
->
+               
mergeReplacedBooleanAttribute(PROPERTY_renderResponseStackTraces, 
env("RestContext.renderResponseStackTraces", false)));
+
+       /**
+        * The request header used for client-version matching; resolved from 
{@code @Rest(clientVersionHeader)},
+        * default {@code "Client-Version"}.
+        */
+       private final Memoizer<String> clientVersionHeader = memoizer(() ->
+               mergeReplacedStringAttribute(PROPERTY_clientVersionHeader, 
env("RestContext.clientVersionHeader", "Client-Version")));
 
        /**
         * The {@link UriRelativity} strategy for URI resolution in this 
resource.
@@ -2290,6 +2202,20 @@ public class RestContext extends Context {
                return getRestAnnotations().stream().findFirst();
        }
 
+       /**
+        * Returns the {@link Rest} annotations for this resource in 
parent-to-child (top-down) order.
+        *
+        * <p>
+        * This is the reverse of {@link #getRestAnnotations()}: most-ancestor 
annotation first,
+        * most-derived last. Use this when annotation values should accumulate 
in inheritance order
+        * (e.g. parent values are applied first and child values append or 
override them).
+        *
+        * @return An unmodifiable list of {@link AnnotationInfo} for {@link 
Rest}, never {@code null}.
+        */
+       protected List<AnnotationInfo<Rest>> getRestAnnotationsTopDown() {
+               return restAnnotationsTopDown.get();
+       }
+
        /**
         * Returns {@code true} if values for the given annotation attribute 
may be inherited from {@link #parentContext}.
         *
@@ -2522,7 +2448,7 @@ public class RestContext extends Context {
         *
         * @return The annotations applied to this context.
         */
-       public AnnotationWorkList getAnnotations() { return 
builder.getApplied(); }
+       public AnnotationWorkList getAnnotations() { return annotationWork; }
 
        /**
         * Returns the bean context associated with this context.
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
index 5603a7e910..51ecdbfe5d 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
@@ -23,7 +23,6 @@ import java.lang.annotation.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
 import org.apache.juneau.commons.annotation.*;
 import org.apache.juneau.config.*;
 import org.apache.juneau.cp.*;

Reply via email to