Sling Model Annotation: Detailed in AEM

Satyam Gupta
11 min readJul 18, 2024

--

Good day, developers Salutations from this blog! With thorough descriptions of each annotation used in the Sling Model in AEM, this blog aims to assist readers in understanding each one.

As we know Sling Model is an annotation driven Java class in AEM.

Visit this blog to learn more about the sling model in depth and with examples: https://satyamblogs.medium.com/sling-model-and-its-implementation-with-custom-component-in-aem-32c7138e6f4b

Below, we’ll discuss a few specific annotations one by one used in the sling model.

@Inject
@ValueMapValue
@Named
@default
@Self
@Via
@PostConstruct
@OSGiService
@SlingObject
@ScriptVariable
@ChildResource
@ResourcePath
@RequestAttribute

@Inject

→ The @Inject annotation is more general and flexible. It can be used to inject various types of objects, including OSGi services, request attributes, resource properties, etc.

→ Services and other dependencies that are not directly connected to the material being mapped but are required for the Sling Model to operate correctly are usually injected using the @Inject annotation.

→You must take care to make sure that the right kind of object is injected into the Sling Model because the @Inject annotation does not offer type safety for the injected dependency.

→ By default, @Inject fields are optional. If the value is not available, the field will be null unless explicitly marked as required.

@Inject is used for dependency injection

@ValueMapValue

→ The @ValueMapValue annotation is more specific. It is used to inject properties directly from the resource. It is useful when you need to map resource properties to model fields directly.

→ Supports default values if the property is not present in the ValueMap.

→ Automatically converts the property to the field’s type if necessary.

@ValueMapValue annotation provides type safety since you can specify the type of the Java field that is being mapped to the JCR property.

Comparison and Use Cases

When to Use @Inject:

  • When injecting OSGi services.
  • When you need more flexibility in the source of the injection (e.g., request attributes, resource resolver, Sling models).
  • When using custom injectors.

When to Use @ValueMapValue:

  • When directly injecting properties from the ValueMap of a resource.
  • When you want automatic default values or type conversion for resource properties.
  • When you want to clearly express that the field should be populated from the ValueMap.

@Named

→ This annotation is used to specify the name of the property in the content tree that should be mapped to the annotated field.

→ This is particularly useful when the field name in the model does not match the property name in the source, or when you want to inject a specific value from a multi-field or a map.

→ When you need to inject specific values based on a custom name.

Example: we want to fetch “jcr:lastModifiedBy” from JCR for the specific component

we can use @named annotation this way:

   @ValueMapValue
@Named("jcr:lastModifiedBy")
private String createdBy;

public String getCreatedBy() {
return createdBy;
}

and the output is:

@default

@Default annotation is used to specify default values for fields in Sling Models when the actual value is not present or cannot be resolved.

Null pointer exceptions are avoided and the models are strengthened and depended upon by ensuring that the model fields have reasonable default values.

→ To provide default values, the @Default annotation is commonly used in conjunction with other Sling Model annotations like @ValueMapValue, @Inject, etc.

Example:

    @ValueMapValue
@Default(values = "Default Title") // Provides a default value for the title field
private String title;

@ValueMapValue
@Default(intValues = 0) // Provides a default value of 0 for the viewCount field
private int viewCount;

@Self

@Self annotation is used in Sling Models to inject the adaptable object itself into a model. This is particularly useful when you need direct access to the original resource, request, or other adaptable object from which the model is created.

→ Provides direct access to the original adaptable object, enabling additional operations or property retrievals.

@Self: Injects the adaptable Resource object into the resource field. This allows the model to access the resource directly for additional operations or property retrievals that are not mapped to specific fields.

Example1:

@Model(adaptables = SlingHttpServletRequest.class)
public class RequestModel {

@Self
private SlingHttpServletRequest request;

public String getRequestPath() {
return request.getRequestURI();
}
}

Example2:

@Model(adaptables = Resource.class)
public class CustomModel {

@Self
private Resource resource;

public boolean hasChildNodes() {
return resource.hasChildren();
}
}

@Via

@Via annotation is used to specify an alternative path or mechanism to access a value.

→ When the requested value may be accessible through another resource or object but is not directly available on the adaptable, this is helpful.

Common Use Cases for @Via

  1. Accessing a Child Resource: When the value is available in a child resource rather than the current resource.
  2. Accessing a Parent Resource: When the value is available in a parent resource.
  3. Accessing a Different Model: When the value is part of a different Sling Model or object.

Enhances the flexibility of Sling Models by allowing access to values not directly available on the adaptable.

Example 1: Accessing a Child Resource

/content/mysite/jcr:content
+ myComponent
+ details
- title: "Hello World"
- description: "This is a description."

------------------------------------------------------------

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class MyComponentModel {

@ValueMapValue
@Via("details") // Specifies that the value should be taken from the "details" child resource
private String title;

@ValueMapValue
@Via("details")
private String description;

}

Example 2: Accessing a Different Model

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.Via;

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ParentModel {

@ValueMapValue
private String title;

public String getTitle() {
return title;
}
}

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ChildModel {

@Self
@Via("parentResource") // Accessing the ParentModel through the parentResource
private ParentModel parentModel;

public String getTitleFromParent() {
return parentModel.getTitle();
}
}

@PostConstruct

@PostConstruct annotation is used to mark a method in a Sling Model that should be executed after the model's dependencies have been injected and the model has been fully initialized.

→ In order to do any further initialization activities that might be required, such as processing or modifying inserted variables, this function acts as a callback.

→ The method annotated @PostConstruct must be void and take no arguments.

→ Allows for additional setup, validation, or processing that depends on the injected values.

Benefits of @PostConstruct

  1. Complex Initialization: Perform complex initialization tasks that require injected values.
  2. Validation: Validate the injected values and set default values if necessary.
  3. Transformation: Transform or process the injected values to prepare them for use in the component.
package com.example.aem.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import javax.annotation.PostConstruct;

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ValidatedModel {

@ValueMapValue
private String title;

@ValueMapValue
private String description;

@PostConstruct
protected void init() {
// Validate and set default values
if (title == null || title.isEmpty()) {
title = "Default Title";
}
if (description == null || description.isEmpty()) {
description = "Default Description";
}
}

public String getTitle() {
return title;
}

public String getDescription() {
return description;
}
}

The init method checks if title and description are null or empty, and if so, assign default values.

@OSGiService

@OSGiService annotation is used to inject OSGi services directly into a Sling Model.

→ The Sling Models API provides this annotation, which makes it easier to access OSGi services and lets you take full advantage of the OSGi service registry inside your Sling Models.

Benefits of @OSGiService

  1. Simplified Service Injection: Makes it easy to inject and use OSGi services within Sling Models without manual service lookup.
  2. Automatic Dependency Management: Ensures that services are available and managed automatically by the OSGi runtime.
  3. Clean and Readable Code: Enhances code readability and maintainability by clearly showing service dependencies.

Example:


@Model(adaptables = SlingHttpServletRequest.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL )
public class StudentConfigurationModelImpl {

@OSGiService
StudentConfigurationMethods studentConfigurationMethods;


public String getStudentName() {
return studentConfigurationMethods.getStudentName();
}


public int getRollNumber() {
return studentConfigurationMethods.getRollNumber();
}

In this example, StudentConfigurationMethods is an OSGI service.

@SlingObject

@SlingObject annotation is used to inject common Sling objects directly into the model.

→ This annotation is provided by the Sling Models API and simplifies the injection of frequently used objects like Resource, SlingHttpServletRequest, SlingHttpServletResponse, ResourceResolver, etc.

Commonly Injected Objects with @SlingObject

  • Resource: Represents the resource being adopted.
  • ResourceResolver: Provides methods to resolve resources.
  • SlingHttpServletRequest: Represents the current request.
  • SlingHttpServletResponse: Represents the current response.
  • SlingScriptHelper: Provides access to various scripting utilities and services.

@SlingObject
private Resource resource; // Injects the current Resource

@SlingObject
private ResourceResolver resourceResolver; // Injects the ResourceResolver

@SlingObject
private SlingScriptHelper scriptHelper; // Injects the SlingScriptHelper

Benefits of @SlingObject

  1. Convenient Injection: Simplifies the injection of common Sling objects, reducing boilerplate code.
  2. Enhanced Readability: Makes the model code more readable and maintainable by clearly showing dependencies.
  3. Direct Access: Provides direct access to commonly used objects without manual retrieval.

@ScriptVariable

@ScriptVariable annotation in AEM is used in Sling Models to inject values from the script context, such as the current page, resource, request, etc.
These values are often available in the HTL (Sightly) script context but not directly available through JCR properties or OSGi services.

→ Injecting current page, current resource, current request, current session, etc.

→ By default, the injection is optional, and if the variable is not available, the field will be null.

Common @ScriptVariable Injections

Here are some common objects you might inject with @ScriptVariable:

  • Page: Represents the current page.
  • Resource: Represents the current resource.
  • SlingHttpServletRequest: Represents the current request.
  • SlingHttpServletResponse: Represents the current response.
  • ValueMap: Represents the properties of the current resource.
  • SlingScriptHelper: Provides access to various AEM services.
  • ResourceResolver: Provides access to resources in the JCR.
import com.day.cq.wcm.api.Page;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class AdvancedExampleModel {

@ValueMapValue
private String title;

@ScriptVariable
private Page currentPage;

@ScriptVariable
private Resource resource;

@ScriptVariable
private ResourceResolver resourceResolver;

@ScriptVariable
private SlingScriptHelper sling;

public String getTitle() {
return title;
}

public Page getCurrentPage() {
return currentPage;
}

public Resource getResource() {
return resource;
}

public ResourceResolver getResourceResolver() {
return resourceResolver;
}

public SlingScriptHelper getSling() {
return sling;
}
}

In this example, we inject several objects from the script context, including the current Page, Resource, ResourceResolver, and SlingScriptHelper.

@ChildResource

A child resource of the current resource can be immediately injected into the model using the @ChildResource annotation. This annotation offers a handy way to traverse and work with the content hierarchy within your Sling Models, making it easier to access and deal with child resources.

→ Allows specifying the path relative to the current resource to identify the child resource.

→ Can inject single resources or collections (e.g., lists of resources).

Here’s an example to illustrate how to use the @ChildResource annotation in a Sling Model:

Step 1: Define the JCR Structure

/content/mysite/jcr:content
+ myComponent
- title: "Main Title"
+ details
- subtitle: "Sub Title"
- description: "Detailed description."
+ items
+ item1
- name: "Item 1"
+ item2
- name: "Item 2"

Step 2: Create the Sling Model

ExampleModel.java:

package com.example.aem.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import java.util.List;

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ExampleModel {

@ValueMapValue
private String title;

@ChildResource(name = "details")
private Resource details; // Injects the 'details' child resource

@ChildResource(name = "items")
private List<Resource> items; // Injects the 'items' child resources

public String getTitle() {
return title;
}

public String getSubtitle() {
return details != null ? details.getValueMap().get("subtitle", String.class) : null;
}

public String getDescription() {
return details != null ? details.getValueMap().get("description", String.class) : null;
}

public List<Resource> getItems() {
return items;
}

public String getItemName(int index) {
if (items != null && items.size() > index) {
return items.get(index).getValueMap().get("name", String.class);
}
return null;
}
}

Explanation:

  • @ChildResource(name = "details"): Injects the details child resource.
  • @ChildResource(name = "items"): Injects a list of child resources from the items node

Benefits of @ChildResource:

  1. Simplified Access: Provides a simple way to access child resources without manual navigation.
  2. Flexible Injection: Supports injecting single resources and collections of resources.
  3. Clean Code: Enhances code readability and maintainability by clearly showing the relationship between resources.

@ResourcePath

@ResourcePath annotation is used to inject a Resource based on a given path.

→ This annotation is useful when you need to work with resources that are not directly part of the current request or resource tree, allowing you to inject resources from any location in the JCR repository.

Example:

JCR Structure-------------------------------------------------

/content/mysite
+ jcr:content
- title: "Main Content"
+ referencedContent
- title: "Referenced Content"


Sling Model------------------------

package com.example.aem.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ResourcePath;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ExampleModel {

@ValueMapValue
private String title;

@ResourcePath(path = "/content/mysite/referencedContent")
private Resource referencedResource; // Injects the resource at the specified path

public String getTitle() {
return title;
}

public String getReferencedTitle() {
if (referencedResource != null) {
ValueMap valueMap = referencedResource.getValueMap();
return valueMap.get("title", String.class);
}
return null;
}
}

Benefits of @ResourcePath

  1. Access Remote Resources: Allows you to access and work with resources located anywhere in the JCR repository.
  2. Flexible and Dynamic: Supports dynamic injection of resources, making your models more flexible.
  3. Simplified Code: Reduces the need for manual resource resolution, simplifying the code and enhancing readability.

@RequestAttribute

→ Request attributes are data that are passed along with the HTTP request, and this annotation allows you to access these attributes directly within your model.

→ This is particularly useful when you need to process or utilize data that is provided dynamically during the request.

Benefits of @RequestAttribute

  1. Simplified Access: Provides a straightforward way to access request attributes within Sling Models.
  2. Dynamic Data: Enables passing and utilizing dynamic data provided during the request.
  3. Cleaner Code: Reduces the need for manual retrieval of request attributes, leading to cleaner and more maintainable code.

Example of Using @RequestAttribute

Step 1: Set Up the Request Attribute
Servlet or Component
: In your servlet or any other part of your code that handles the request, set the request attribute:

request.setAttribute("myAttribute", "This is a request attribute");

Step 2: Create the Sling Model

ExampleModel.java:

package com.example.aem.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.RequestAttribute;

@Model(
adaptables = SlingHttpServletRequest.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ExampleModel {

@RequestAttribute(name = "myAttribute")
private String myAttribute; // Injects the request attribute

public String getMyAttribute() {
return myAttribute;
}
}

@RequestAttribute(name = "myAttribute"): Injects the request attribute named myAttribute into the Sling Model.

Now you can understand all the annotations that are used in the Sling Model in AEM.

I hope you found this article interesting and informative. Please share it with your friends to spread the knowledge.

You can follow me for upcoming blogs follow.

Thank you!

--

--

Satyam Gupta
Satyam Gupta

Written by Satyam Gupta

AEM Full Stack Developer | Adobe Analytics Developer | Java

Responses (1)