REST Server — Children vs Mixins
Juneau offers two ways to compose one resource's operations into another:
@Rest(children=...) (plus its host-side seeding form
@Rest(childrenDefs=@Child(...)))
and @Rest(mixins=...) (plus its host-side override
form @Rest(mixinDefs=@Mixin(...))).
They look superficially similar — both pull a second class's @RestOp methods into a parent, and
both let a host reach in and adjust settings for the composed class — but the underlying resolution
models are opposite, and picking the wrong one causes subtle routing and configuration surprises.
The one-line rule:
Mixins are inline — they share the host's URL namespace and inherit its configuration. Children are isolated — they mount at their own URL prefix and resolve configuration independently. A host can explicitly seed a curated set of settings onto a child (10.0.0), but that's additive opt-in, not inheritance.
This page is the canonical "which one do I reach for" reference.
Decision guide
- Reach for a mixin when the second class is an inline extension of the host: convention
endpoints (health, info, swagger, favicon), cross-cutting ops that should share the host's
serializers/guards/messages, or anything you'd otherwise copy-paste into the host class. Use
@Mixin(...)when the host wants to override a setting (e.g. guard) for that mixin's endpoints. - Reach for a child when the second class is a standalone sub-resource with its own URL subtree
and its own configuration lifecycle — often a separately-deployable REST endpoint that just happens
to be discovered through a parent. If the host needs to impose a handful of settings on that child
(an admin guard, a shared call logger) without editing the child class, use
@Child(...)— see Host-side seeding with@Childfor the full seed-member table and semantics.
Same vs. different matrix
| Axis | Mixin (@Rest(mixins=) / @Rest(mixinDefs=@Mixin)) | Child (@Rest(children=) / @Rest(childrenDefs=@Child)) |
|---|---|---|
| Path composition | Op merges into the host's operation table and resolves at its own @RestOp(path) under the host namespace — no auto-inserted segment. (A host @Mixin(path=...) can re-mount the mixin's ops under a chosen prefix.) | Child contributes a path segment from the child's @Rest(path); the request is re-dispatched to the child with a trimmed path remainder. |
RestContext | Own sub-context, parent-linked to the host (isMixinContext). | Own context, isolated (no parent walk). |
Config inheritance (serializers, parsers, guards, roleGuard/rolesDeclared, converters, encoders, responseProcessors, restOpArgs; partSerializer/partParser; messages) | Inherited from the host: list-shaped props = host chain then mixin appended; replace-shaped props = mixin value wins else host inherited; messages = mixin bundle chained under the host's. | Isolated by default: the child resolves each property from its own @Rest only; nothing inherits from the parent automatically. A host can opt in to seed a curated subset (see below) via @Rest(childrenDefs=@Child(...)) — everything else stays isolated. |
noInherit | Meaningful — cuts the host→mixin walk per property (mixin class's @Rest(noInherit=...) and/or the host @Mixin(noInherit=...), unioned). | No noInherit() member on @Child itself (there's no inherited chain to cut) — but a child's own @Rest(noInherit="<property>") cuts the corresponding host-seeded value too, for every seedable property. |
| Host-side override / seed | @Rest(mixinDefs=@Mixin(type=X, guards=..., ...)) — the host overrides settings on the mixin's already-inherited chain; the host's value wins outright over the mixin class's own declaration. | @Rest(childrenDefs=@Child(type=X, guards=..., ...)) (10.0.0) — the host seeds a curated set of settings onto the child's otherwise-isolated context. This is not an override: additive-security members (guards, converters, roleGuard, rolesDeclared) stack alongside the child's own (prepend / AND-stack), and child-wins scalars (partSerializer, partParser, defaultCharset, maxInput) apply only when the child is silent — the child's own explicit @Rest always wins. See Host-side seeding with @Child. |
| Operation discovery / collision | Single merged op table; host wins over mixin on path+method collision; mixin-vs-mixin resolved by mixins= declaration order. | Separate RestChildren registry matched by path prefix; child ops never merge into the host table. |
Request routing / getPathInfo | Mixin op sees the host request unchanged (same servletPath/pathInfo). | Child sees a rewritten servletPath/pathInfo (the matched segment is consumed). |
| Matching order | Ops (host + mixin) matched as one table. | Children are matched before the host's own ops. |
BeanStore / DI | Mixin sub-context's bean store is parent-linked to the host's (inherits host beans). | Child's bean store is independent (resolves its own beans). |
| Swagger / OpenAPI | Mixin ops appear on the host's generated Swagger. | Child has its own Swagger subtree. |
| Instance identity | Separate instance from the host; mixin ops run on the mixin instance within the host's request flow. | Separate instance; child runs its own request flow. |
Lifecycle hooks (@RestStartCall/@RestPreCall/@RestPostCall/@RestEndCall/@RestDestroy) | Dual-fire host-then-mixin for mixin-endpoint requests. | Child runs its own hooks only. |
| Transitive composition | Nested mixins parent-link flat to the host (not chained); a mixin's @Rest(mixins=B) discovers B at the host level. | Children nest as a normal resource tree (each child can have its own children). |
Why the divergence is intentional
The rationale behind the mixin sub-context model:
"Children are completely independent from parents — they're externally-mounted at their own URL namespace, often have their own deployment lifecycle (factored into separate jars, etc.), and conflating their resolution with the host's would surprise authors who treat children as standalone REST endpoints that happen to be discovered through a parent. Mixins are different: they're explicitly inline, share the host's URL namespace, and exist to compose with the host. Inheritance is the right model for mixins; isolation is the right model for children."
Because the resolution models are opposite, the per-property mixin precedence (the host @Mixin
override → mixin class's @Rest → inherited host chain, gated by noInherit) has no exact child
analog — a child never inherits a chain from the host, so there's nothing for a host to "override."
What a host can do, as of 10.0.0, is seed settings directly onto an isolated child via
@Rest(childrenDefs=@Child(...)) — a deliberately different mechanism from a mixin override, not a
lesser version of it. A @Mixin override wins outright over an already-inherited value; a @Child
seed can only add to or fill a gap in the child's own config, and the child's own explicit
declaration always has the final say. See Host-side seeding with
@Child in Child Resources for the
full seed-member table, the additive-security/child-wins-scalar split, and the noInherit interaction
— that section is the canonical reference for the mechanism; this page only summarizes it for the
mixin-vs-child comparison above.
See also
- REST Server — Mixins and Multi-Mount Paths
- REST Server — Mixin Sub-Contexts — the full inheritance model and
@Mixinhost-side overrides - REST Server — Standalone vs Mixin Resources
- Child Resources