Skip to main content

juneau-rest-server-springboot

Maven
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-server-springboot</artifactId>
<version>${juneau.version}</version>
</dependency>
Jarjuneau-rest-server-springboot-10.0.0.jar
OSGiorg.apache.juneau.rest.server.springboot_10.0.0.jar
LinksMaven Central · Javadocs · Source

Contents/Features​

The juneau-rest-server-springboot library provides classes to make it easy to integrate Juneau REST resources with Spring and Spring Boot.

See the guide: Build a REST API on Spring Boot

Auto-Configuration Classes​

The module registers two Spring Boot auto-configuration classes through META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (the Spring Boot 3 mechanism; the module ships no spring.factories and no @ConfigurationProperties classes):

ClassGateContributes
JuneauRestAutoConfigurationjuneau.rest.auto-register=true (off when the property is absent)A ServletContextInitializer bean, juneauRestAutoRegistrar, that mounts every RestServlet bean at its self-declared top-level path.
JuneauRestLoggingAutoConfigurationLogback's LevelChangePropagator on the classpath, and juneau.rest.logging.propagate-levels not set to falseA Runnable bean, juneauRestLogLevelPropagatorInstaller (replaceable by declaring a bean of that name), plus a SmartInitializingSingleton that runs it.

Explicit ServletRegistrationBean registrations always win over auto-registration, and the JuneauServletRegistrations.forServlet(servlet, beanStore) factory is the explicit alternative. See REST Server - Self-Registration.

Configuration Properties​

All properties are read from the Spring Environment (application.properties, application.yaml, environment variables, command line). The module defines only the following:

PropertyTypeDefaultEffect
juneau.rest.auto-registerbooleanabsent (disabled)When true, activates JuneauRestAutoConfiguration.
juneau.rest.logging.propagate-levelsbooleantrue (matchIfMissing)When false, JuneauRestLoggingAutoConfiguration contributes nothing.
juneau.rest.logging.propagate-levels.reset-julbooleanfalsePassed to Logback's propagator as setResetJUL; see the reset-jul section below.
juneau.profiles.activecomma-delimited stringSpring's active profilesNot an auto-configuration property. SpringEnvironmentPropertySource answers it from Environment.getActiveProfiles() unless the property is set explicitly, in which case the explicit value wins.
juneau.spring.missCache.disablebooleanfalseNot an auto-configuration property. When true, SpringEnvironmentPropertySource keeps no miss cache and walks Spring's property sources on every lookup. Read once, on the bridge's first lookup, from the Spring Environment itself (so application.properties, @TestPropertySource, command line, the exact system property, or JUNEAU_SPRING_MISSCACHE_DISABLE); later changes are ignored.
# application.properties
juneau.rest.auto-register=true
juneau.rest.logging.propagate-levels=true
juneau.rest.logging.propagate-levels.reset-jul=false

Bean Precedence​

When a Juneau REST resource runs inside a Spring Boot application, framework-managed beans on RestContext (RestDebugFormatter, EncoderSet, SerializerSet, ParserSet, ThrownStore, Config, VarResolver, HttpPartSerializer, HttpPartParser, etc.) are resolved through the resource's SpringBeanStore in the following order — the first match wins:

  1. Local entries — beans registered directly on the resource's own bean store, most commonly a @Bean method/field on the @Rest-annotated resource class (org.apache.juneau.commons.inject.Bean), or anything added programmatically via addBean(...) / addSupplier(...).
  2. Regular parent bean store, if any — the store passed as the parent constructor argument when the SpringBeanStore was created (SpringBeanStore(ApplicationContext, BeanStore)).
  3. Memoizer-backed framework default — Juneau's built-in implementation (BasicRestDebugFormatter, BasicEncoderSet, etc.), registered via addDefaultSupplier(...).
  4. Spring @Bean — reached only if none of the above resolved the type (and name, where applicable), via the Spring ApplicationContext.

This is a fallback relationship, not an override: SpringBeanStore extends BasicBeanStore, and its overridden getBean() / getBeanSupplier() / hasBean() / getBeansOfType() methods each call super.getBean(...) (which resolves tiers 1–3 above) first, and only query Spring's ApplicationContext.getBeanProvider(...) / getBean(...) when that returns empty. Concretely, a @Bean method declared directly on the resource class always wins over an identically-typed Spring bean — this is confirmed directly by SpringBeanStore_Test.c05_getBean_localOverridesSpring, which registers a local bean, stubs an identically-typed bean on a mocked ApplicationContext, asserts that the local bean is returned, and asserts verifyNoInteractions(mockAppContext) — proving Spring is never even queried once the local lookup succeeds.

Practical implications:

  • To let a Spring bean take effect, don't declare a competing @Bean method/field of the same type (and name) on the resource class — a local declaration always shadows the Spring bean, regardless of any @Primary/@ConditionalOn... annotations on the Spring side.
  • To force your resource-class @Bean to win, no action is needed — this is the default behavior.
  • Spring-native qualifiers still matter, but only among Spring candidates. @Primary, @Qualifier, and @ConditionalOn... control which Spring bean SpringBeanStore sees when it falls back to Spring (step 4) — they have no effect if a local entry, parent-store entry, or framework default already satisfied the lookup.

BasicBeanStore separately supports a real overriding-parent slot — a store consulted before local entries — via the BasicBeanStore(BeanStore parent, BeanStore overridingParent) constructor or, for builder-style consumers, the BeanStoreOverridable<B>.overridingBeanStore(BeanStore) interface (implemented by builders such as SerializerSet.Builder, ParserSet.Builder, EncoderSet.Builder, and MockRestClient.Builder — mainly for test-time bean substitution). SpringRestServlet.createBeanStore(...) does not use this slot for the Spring bridge: it always constructs new SpringBeanStore(appContext, parent) with parent as the regular (non-overriding) parent, so Spring is layered in only as the last-resort fallback described above, never as a true override.

Property Resolution​

Installing SpringBeanStore also bridges the Spring Environment into Juneau's Settings singleton (via SpringEnvironmentPropertySource), so @Value("${...}") placeholders on your beans resolve against the same property space Spring uses — application.yaml / application.properties, environment variables, command-line arguments, and any custom Spring PropertySource. Spring's active profiles are also surfaced to Juneau's juneau.profiles.active config-profile key. See the cross-links below for details.

There is one bridge per ApplicationContext, however many Spring REST servlets share the context. It is added when the first SpringBeanStore for the context is built, keeps that position in the Settings chain, and is removed when the context closes (any ConfigurableApplicationContext) or when every SpringBeanStore on it has been clear()ed. A Settings source added between two servlets' construction therefore wins over the bridge for keys both define.

Juneau's own builder keys (MarshallingContext.unsortedProperties, Serializer.*, ...) are mixed-case, so Spring Boot cannot parse them as configuration property names and walks every raw property source for each lookup. The bridge remembers up to 4,096 such keys that were absent. It checks the Environment's property-source list on every lookup, so adding, removing or replacing a PropertySource is seen at once, and a cached key that is later set as a system property is found. Valid Spring Boot names (lowercase and dot-separated, such as my.app.key) and hits are never cached.

The one change the cache does not see is a property source whose contents change in place (a mutated MapPropertySource map, or MockEnvironment.setProperty in a test), for a mixed-case key that Juneau already looked up and missed. After such a change, call SpringBeanStore.invalidateEnvironmentCache(applicationContext), or set juneau.spring.missCache.disable=true.

Logback level propagation to JUL​

When Logback is the active backend, juneau-rest-server-springboot auto-installs a JUL LevelChangePropagator listener by default (juneau.rest.logging.propagate-levels=true, matchIfMissing=true) so logging.level.* changes are reflected into JUL-backed REST debug detail controls.

  • Installation is guarded at runtime:
    • if Logback classes are absent, no install is attempted;
    • if ILoggerFactory is not a Logback LoggerContext, it silently no-ops.
  • Disable propagation with juneau.rest.logging.propagate-levels=false.
  • The installer is additive and idempotent (it reuses any existing LevelChangePropagator subtype, and installs at most one reset-resistant listener).

reset-jul posture​

juneau.rest.logging.propagate-levels.reset-jul defaults to false (recommended).

  • false (default): pre-existing JUL logger levels/handlers are preserved.
  • true: process-global reset/reapply behavior from Logback's propagator is enabled.

When reset-jul=true, be explicit about the side effects:

  • non-root explicit JUL logger levels are cleared;
  • explicit Logback levels are then propagated back into JUL;
  • JUL-only logging.properties entries not mirrored by explicit Logback levels do not survive.

For operator-controlled durability across explicit Logback resets, you can also install a Logback XML <contextListener> directly.

See also​