Study guide
4.1 Sling Models Framework, Injectors & Model Exporter
Adobe Certified Professional - AEM Sites Developer AD0-E128
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Dispatcher Farm & Multi-Site Configuration
1.1 AEM Dispatcher Configuration & Caching Fundamentals 1.2 Multi-Site Management (MSM) & Blueprint/Live Copy Architecture 1.3 OSGi Configuration Management & Run Modes (.cfg.json)
2 Chapter 2: Oak Indexing & Custom Logging
2.1 Custom Oak Index Definitions & Lucene Query Optimization 2.2 AEM Custom Logging, Logback Configurations & Request Tracing
3 Chapter 3: Component Architecture, HTL & Client Libraries
3.1 Core Components & Proxy Component Pattern 3.2 HTL (HTML Template Language) Block Statements & Context-Aware Escaping 3.3 Client-Side Libraries (Clientlibs) Architecture & Management
4 Chapter 4: Sling Models & OSGi Declarative Services
4.1 Sling Models Framework, Injectors & Model Exporter 4.2 OSGi Declarative Services, Component Lifecycle & Service Rankings
5 Chapter 5: Templates, Fragments & Business Workflows
5.1 Editable Templates, Structure, Initial Content & Content Policies 5.2 Content Fragments, Experience Fragments & Headless Delivery 5.3 AEM Workflows, Custom Process Steps & Launchers
6 Chapter 6: Build, Packaging & Cloud Manager CI/CD
6.1 Maven Project Structure & AEM Project Archetype 6.2 Content Package Types, Packaging Rules & FileVault Validation 6.3 Cloud Manager CI/CD Pipelines & Quality Gates 6.4 Rapid Development Environments (RDE) & Sling Repoinit Scripts
7 Chapter 7: Environment Maintenance & Content Distribution
7.1 AEM Maintenance Releases & Cloud Service Version Updates 7.2 AEM Deployment Topologies & Cloud Architecture 7.3 Replication Agents & Sling Content Distribution 7.4 Repository Maintenance: Revision Cleanup, Compaction & Purge Tasks
8 Chapter 8: Analyzing, Debugging & Diagnostic Tools
8.1 System Log Analysis & Diagnostic Tooling 8.2 Debugging Client-Side Libraries & Granite Dumplibs 8.3 Dispatcher Caching Diagnostics, X-Cache-Info & Local Validation 8.4 Environment Troubleshooting, Web Console & Developer Console
4.1 Sling Models Framework, Injectors & Model Exporter
Key Takeaways Explain This Topic AI
Sling Models are annotation-driven POJOs or interfaces that map JCR resources or HTTP requests to Java objects, eliminating boilerplate JCR API calls and manual type casting.
The choice of adaptables (Resource.class vs SlingHttpServletRequest.class) dictates injector scope: request-adapted models access request attributes, query parameters, and scripting variables, whereas resource-adapted models are lighter and decoupled from the HTTP layer.
Specialized injector annotations (@ValueMapValue, @ChildResource, @Self, @OSGiService, @RequestAttribute, @ScriptVariable) replace generic @Inject to provide deterministic, compile-safe, and high-performance injection.
Setting defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL on @Model prevents complete model adaptation failure when authored properties are missing in the JCR repository.
The Sling Model Exporter framework utilizes Jackson serialization (@Exporter(name = "jackson", extensions = "json")) to expose structured content via the .model.json selector for Headless CMS and SPA Editor integration.
Last updated: September 2026
4.1 Sling Models Framework, Injectors & Model Exporter
Exam Focus: Apache Sling Models represent the standard backend programming model for Adobe Experience Manager Sites. The AD0-E128 exam tests your mastery of @Model configuration parameters ( adaptables , adapters , defaultInjectionStrategy ), specialized injector annotations ( @ValueMapValue , @ChildResource , @Self , @OSGiService , @RequestAttribute , @ScriptVariable ), post-construction lifecycle management ( @PostConstruct ), the Sling Model Exporter ( @Exporter with Jackson for .model.json ), and comprehensive unit testing using AEM Mocks ( AemContext ) .
Architectural Philosophy of Apache Sling Models
Historically, AEM developers extracted content from the Java Content Repository (JCR) using low-level JCR APIs ( javax.jcr.Node , javax.jcr.Property ) or Apache Sling ValueMap decorators in JSP or Java Use-API classes ( WCMUsePojo ). This legacy paradigm forced developers to write repetitive, error-prone boilerplate:
Explicit type casting and null checks ( if (properties.get("title", String.class) != null) ... ).
Manual traversal of child nodes and sub-resources.
Tightly coupled scripting contexts and unhandled repository exceptions.
Inability to unit test presentation logic without running a full OSGi integration container.
Introduced to solve these challenges, Apache Sling Models is an annotation-driven Object-to-Content Mapping (OCM) framework. Sling Models are standard Plain Old Java Objects (POJOs) or interfaces that bind repository resources or HTTP requests to strongly typed Java instances via reflection and runtime injectors. By offloading resource resolution, type coercion, and service lookup to the framework, developers achieve clean separation of concerns, testability, and enterprise-grade maintainability.
The @Model Annotation & Core Parameters
Every Sling Model class or interface must be annotated with @Model ( org.apache.sling.models.annotations.Model ). The annotation accepts several critical parameters that define how and where the model can be instantiated.
1. adaptables : Resource.class vs SlingHttpServletRequest.class
The adaptables parameter defines what context object can be adapted into the model:
adaptables = Resource.class : The model adapts directly from an org.apache.sling.api.resource.Resource .
adaptables = SlingHttpServletRequest.class : The model adapts from an org.apache.sling.api.SlingHttpServletRequest .
Architectural Attribute adaptables = Resource.class adaptables = SlingHttpServletRequest.class
Context Scope Scoped strictly to the JCR resource node. Scoped to the active HTTP request context.
Available Injectors @ValueMapValue , @ChildResource , @Self (Resource), @OSGiService . All resource injectors plus @RequestAttribute , @ScriptVariable , request parameters, headers, cookies, selectors, and suffix.
HTL Usage <div data-sly-use.m="MyModel"> (adapts current resource). <div data-sly-use.m="MyModel"> (adapts current request).
Off-Request Adaptability Can be adapted anywhere: Sling Jobs, OSGi Event Listeners, Workflow Process steps, scheduled tasks. Cannot be adapted outside an active HTTP request (will return null ).
Unit Testing Overhead Minimal; requires only mock resources in AemContext . Requires mock HTTP request, response, attributes, and request parameters.
Architectural Recommendation Default choice: Use unless HTTP request data or HTL arguments are explicitly needed. Required when reading HTL template parameters, query parameters, or scripting objects like currentPage .
2. adapters & Interface-Driven Design
Enterprise AEM development mandates separating public interface contracts from internal implementation classes: package com.myproject.core.models;
- public interface Teaser {
- String getTitle();
- String getDescription();
- String getLinkUrl();
- boolean isActionEnabled();
- }
The implementation class implements the interface and declares it in the adapters array: package com.myproject.core.models.impl;
- import com.myproject.core.models.Teaser;
- import org.apache.sling.api.resource.Resource;
- import org.apache.sling.models.annotations.Model;
- import org.apache.sling.models.annotations.DefaultInjectionStrategy;
- @Model(
- adaptables = Resource.class,
- adapters = Teaser.class,
- resourceType = "myproject/components/content/teaser",
- defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
- )
- public class TeaserImpl implements Teaser {
// fields and getters...
}
HTL scripts reference the public interface: data-sly-use.teaser="com.myproject.core.models.Teaser" . The Sling Models runtime discovers TeaserImpl registered as an adapter for Teaser.class and instantiates the implementation transparently.
3. defaultInjectionStrategy : Handling Null Properties
By default, Sling Models operate under DefaultInjectionStrategy.REQUIRED . If any field in the class fails to inject (because the property does not exist on the JCR node and no @Default is defined), the framework considers the adaptation an error:
Critical Rule: Under DefaultInjectionStrategy.REQUIRED , if a single non-optional injection fails, the entire model adaptation fails and returns null .
In content authoring, editors frequently leave optional dialog fields unauthored. If a model containing ten fields fails completely because one optional text field is empty, the entire component disappears from the page.
Therefore, the enterprise standard is to declare: defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
When OPTIONAL is set at the class level, missing properties inject as null (or default primitive values: 0 , false ), allowing the model to adapt successfully. Individual fields that are strictly mandatory can be marked with the @Required annotation.
Dedicated Injector Annotations
While early versions of Sling Models used the generic @Inject annotation, modern AEM development should prefer injector-specific annotations from org.apache.sling.models.annotations.injectorspecific.* .
Generic @Inject makes the injection source less explicit and can require trying multiple injectors. Injector-specific annotations document intent, reduce ambiguity, and produce clearer diagnostics.
- 1. @ValueMapValue : Injecting Node Properties
- Injects properties from the adaptable's ValueMap : @ValueMapValue
- private String title;
- // Inject property with custom name and fallback
- @ValueMapValue(name = "jcr:title")
- @Default(values = "Default Title")
- private String pageTitle;
- @ValueMapValue
- @Named("cq:lastModifiedBy")
- private String modifiedBy;
- 2. @ChildResource : Injecting Sub-Nodes and Collections
Injects child nodes as Resource , ValueMap , or another nested Sling Model. This is the cornerstone of authoring multifields and composite components: // Injects a single child resource adapted to a child model
- @ChildResource(name = "image")
- private ImageModel image;
- // Injects child nodes under 'items' as a list of LinkItem models (Multifield pattern)
- @ChildResource(name = "items")
- private List<LinkItem> items;
- 3. @Self : Injecting the Adaptable
Injects the adaptable itself into the model or adapts the current adaptable into another object: // Injecting the underlying Resource in a Resource-adapted model
- @Self
- private Resource resource;
- // In a SlingHttpServletRequest model, adapting the request's resource to Core Component Teaser
- @Self
- @Via(type = ResourceSuperType.class)
- private com.adobe.cq.wcm.core.components.models.Teaser coreTeaser;
- 4. @OSGiService : Injecting OSGi Services
- Injects active OSGi services into the model. Supports optional LDAP target filters: @OSGiService
- private ModelFactory modelFactory;
- @OSGiService(filter = "(service.ranking>=500)")
- private PaymentGatewayService paymentService;
- 5. @RequestAttribute : Capturing HTL Parameters
When adapting from SlingHttpServletRequest.class , @RequestAttribute captures parameters passed from HTL data-sly-use or data-sly-template : <!-- HTL Markup passing dynamic parameters -->
- <div data-sly-use.card="${'com.myproject.core.models.Card' @ cardTheme='dark', maxItems=5}">
- <p class="theme-${card.cardTheme}">Displaying ${card.maxItems} items</p>
- </div> @Model(adaptables = SlingHttpServletRequest.class, adapters = Card.class)
- public class CardImpl implements Card {
- @RequestAttribute(name = "cardTheme")
- @Default(values = "light")
- private String cardTheme;
- @RequestAttribute(name = "maxItems")
- @Default(intValues = 3)
- private int maxItems;
- }
- 6. @ScriptVariable : Injecting Scripting Objects
- Injects standard AEM/Sling scripting objects into request-adapted models: @ScriptVariable
- private Page currentPage;
- @ScriptVariable
- private ResourceResolver resourceResolver;
- @ScriptVariable
- private Style currentStyle;
- @ScriptVariable
- private PageManager pageManager;
- Post-Construction Lifecycle: @PostConstruct
In many components, raw injected values require post-processing: assembling complex URLs, computing date formats, validating business rules, or loading secondary resources.
The @PostConstruct annotation ( javax.annotation.PostConstruct ) designates a method executed immediately after all field injections are complete : @PostConstruct
- protected void init() {
- if (StringUtils.isNotBlank(linkUrl)) {
- // Resolve vanity URLs or external links
- this.formattedUrl = resourceResolver.map(linkUrl);
- }
- this.hasValidData = StringUtils.isNotBlank(this.title) && this.formattedUrl != null;
- }
- Exception Handling & Failure Rules in @PostConstruct
Understanding @PostConstruct error mechanics is critical for the exam:
Uncaught Exceptions Abort Adaptation: If an unhandled runtime exception ( NullPointerException , IllegalStateException ) is thrown inside @PostConstruct , the Sling Models runtime aborts adaptation and returns null .
Checked Exceptions Forbidden: The @PostConstruct method cannot declare checked exceptions in its method signature. Any checked exceptions from underlying APIs (e.g., RepositoryException ) must be caught internally: @PostConstruct
- protected void init() {
- try {
- performComplexInitialization();
- } catch (Exception e) {
- LOG.error("Failed to initialize model for resource: {}", resource.getPath(), e);
- // Set safe fallback state rather than rethrowing
- this.items = Collections.emptyList();
- }
- }
- Sling Model Exporter & Headless Delivery ( .model.json )
The Sling Model Exporter framework enables Sling Models to serialize directly into JSON (or XML) representations. This is the foundational technology powering the AEM SPA Editor (React/Angular), Headless CMS delivery, and content synchronization with mobile applications.
- 1. The @Exporter Annotation
- To expose a model through the exporter, annotate the class with @Exporter : @Model(
- adaptables = {SlingHttpServletRequest.class, Resource.class},
- adapters = {Teaser.class, ComponentExporter.class},
- resourceType = "myproject/components/content/teaser",
- defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
- )
- @Exporter(name = "jackson", extensions = "json", options = {
- @ExporterOption(name = "SerializationFeature.WRITE_DATES_AS_TIMESTAMPS", value = "false")
- })
- public class TeaserImpl implements Teaser, ComponentExporter {
- @ValueMapValue
- private String title;
- @ValueMapValue
- private String description;
- @ValueMapValue
- @JsonIgnore
- private String internalTrackingKey; // Excluded from exported JSON
- @JsonProperty("heading")
- @Override
- public String getTitle() {
- return title;
- }
- @Override
- public String getExportedType() {
- return "myproject/components/content/teaser";
- }
- }
- 2. Exporter Annotations & Capabilities
@JsonProperty("key") : Overrides the JSON property key in the serialized payload.
@JsonIgnore : Strictly suppresses sensitive, internal, or circular references from appearing in the JSON output.
@JsonInclude(JsonInclude.Include.NON_NULL) : Suppresses null properties to keep the JSON payload lightweight.
ComponentExporter : Interface required by the AEM SPA Editor. Implementing getExportedType() returns the sling:resourceType so front-end SPAs know which JavaScript component to mount.
3. Exposing Models via .model.json
When @Exporter(name = "jackson", extensions = "json") is registered with a resourceType , Apache Sling's Model Exporter Servlet automatically intercepts requests matching: https://www.example.com/content/myproject/us/en/jcr:content/root/teaser.model.json It executes Jackson serialization on the adapted model and streams the JSON response with Content-Type: application/json .
Unit Testing Sling Models with AEM Mocks ( AemContext )
Unit testing Sling Models without starting a live AEM instance is achieved using the open-source io.wcm.testing.aem-mock (AEM Mocks) library and JUnit 5.
AemContext simulates an in-memory JCR repository (Apache Sling ResourceResolver Mock or Oak Mock), OSGi bundle context, and Sling HTTP request/response pipeline: import io.wcm.testing.mock.aem.junit5.AemContext;
- import io.wcm.testing.mock.aem.junit5.AemContextExtension;
- import org.junit.jupiter.api.BeforeEach;
- import org.junit.jupiter.api.Test;
- import org.junit.jupiter.api.extension.ExtendWith;
import static org.junit.jupiter.api.Assertions.*;
- @ExtendWith(AemContextExtension.class)
- class TeaserModelTest {
private final AemContext context = new AemContext();
- @BeforeEach
- void setUp() {
- // Register Sling Model classes
- context.addModelsForClasses(TeaserImpl.class);
- // Load mock JSON content into simulated JCR
- context.load().json("/com/myproject/core/models/TeaserTest.json", "/content/teaser");
- }
- @Test
- void testTeaserModelAdaptation() {
- // Set current resource context
- context.currentResource("/content/teaser");
- // Adapt resource to model interface
- Teaser teaser = context.currentResource().adaptTo(Teaser.class);
- assertNotNull(teaser, "Teaser model must adapt successfully");
- assertEquals("Summer Campaign", teaser.getTitle());
- assertTrue(teaser.isActionEnabled());
- }
- }
This testing pattern runs in milliseconds within standard Maven mvn test phases, enforcing continuous code quality gates in Cloud Manager CI/CD pipelines.