Sling Models deep dive
Purpose: Reference for injection, adaptables, exporters, delegation and model pitfalls beyond the Session 5 basics.
Who this page is for
| Audience | Why it matters to you |
|---|---|
| Backend engineers | Daily reference |
| Frontend engineers | Read-fluency |
Adaptable choice
| Adaptable | Use when | Trade-off |
|---|---|---|
| Resource | Pure content adaptation | Simplest, most testable, cacheable |
| SlingHttpServletRequest | You need selectors, params, WCM context | Heavier; don't cache derived state across requests |
If a model only reads content, adapt from Resource. Add @Via and @Self composition before reaching for request adaptation.
Injectors reference
| Annotation | Source | Notes |
|---|---|---|
| @ValueMapValue | Property of current resource | @Named for property name ≠ field name |
| @ChildResource | Child node(s) | Inject as Resource, model class, or List thereof |
| @Self | The adaptable | Also: adapt the adaptable to another model |
| @OSGiService | Service registry | Optional=true for soft deps |
| @ScriptVariable | currentPage, pageManager, wcmmode… | Request-adaptable only |
| @RequestAttribute | Request attribute | For values passed via data-sly-resource requestAttributes |
| @SlingObject | resolver, response, etc. | Utility injection |
Patterns
Delegation (extending Core Components)
Wrap the product model rather than re-implementing it:
@Model(adaptables = SlingHttpServletRequest.class, adapters = Title.class,
resourceType = "phi-academy/components/title")
public class PhiTitleModel implements Title {
@Self @Via(type = ResourceSuperType.class)
private Title delegate; // product implementation
@Override public String getText() { // decorate one method
return "PHI | " + delegate.getText();
}
// …delegate everything else
}
Exporter
@Exporter(name="jackson", extensions="json") + resourceType binding → .model.json output. Control the shape with Jackson annotations; never expose repository internals (paths, node names) you don't want to commit to as API.
Pitfalls
| Pitfall | Consequence | Fix |
|---|---|---|
| REQUIRED injection default | Model is null in HTL when any field missing | OPTIONAL strategy + @Default |
| Logic in constructors | Runs before injection — NPEs | Use @PostConstruct |
| Stateful/mutable models | Models are per-request; sharing state breaks | Keep them read-only adapters |
| Heavy work in getters | Repeated per HTL access | Compute once in @PostConstruct |
| Adapting via resolver.adaptTo in loops | Performance | Batch/query smarter, adapt once |