AEM Developerの皆様、どうも。大和株式会社の狩野です。
Sling Modelsにおいて重要な概念であるAnnotationについて解説いたします。 1つ1つの解説とサンプルコードが意外と分量があり、何度かに渡って更新したいと思います。 今回がPart.01となります。
Annotationという言葉の意味、概要
辞書的にいうと、「Annotation」という単語には「注釈」という意味があります。 転じて、プログラミング的には、変数やメソッドに注釈を付けて意味をもたせるという役割があります。
コードで説明すると、以下のようになります。
@Component( service = Servlet.class, property = { "sling.servlet.methods=post", "sling.servlet.paths=" + "/bin/any/path", } ) public class TutorialClass extends SlingAllMethodsServlet { // any code }
TutorialClass に @Component というAnnotationを付与することによって、このクラスはComponentとして扱われます。
丸かっこ内に書かれた記述によって、この TutorialClass はServletであり、Postメソッドで /bin/any/path というパスに対してリクエストを受けたときに動作しますという意味です。
更に、以下のようなこともできます。
@Model( adaptables = {SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL )public class TutorialClass2 { @ValueMapValue private String rootPath; }
2つ目の例では、 @ValueMapValue Annotationにより、ダイアログに入力した値をこれだけの記述で取得することができます。
一旦 properties などで取ってきて、そこから properties.get() とかする必要すら無く、記述量も少なく済むので是非覚えましょう。
Sling Modelsで使われる各Annotationについての説明
@Model
org.apache.sling.models.annotations.Model
@Model Annotationは、付与したクラスをSling Modelとして定義します。
以下の引数を与えます。
| 引数名 | 型 | 効果 | 必須 or 任意 |
|---|---|---|---|
| adaptables | 任意のクラスの配列 | 適応可能なクラス一覧。多くのオブジェクトをカバーしているSlingHttpServletRequest.class を指定するのが望ましい |
必須 |
| adapters | 任意のクラスの配列 | 付与したクラスのフーパークラスやインターフェースを設定する | 任意 |
| defaultInjectionStrategy | DefaultInjectionStrategy.class | Sling Modelに挿入されたフィールドが必須かオプションかを決める。デフォルトは必須 | 任意 |
| resourceType | 文字列 | Sling Modelsのリソースタイプ | 任意 |
サンプルコード
よくある設定。
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { protected static final String RESOURCE_TYPE = "myapp/any/sling/resourcetype"; // any code }
@Inject
javax.inject.Inject
@Inject Annotationは、付与したフィールドにプロパティを挿入します。
代替のAnnotationが存在しているので、基本的に使わないほうがよい。
サンプルコード
以下のように @Inject Annotationを付与すると、 resourceResolver を以降のコードで使うことができるようになる。
しかし、 resourceResolver を使う場合は下の @SlingObject アノテーションを使ったコードを使うほうが望ましい。
@Inject private ResourceResolver resourceResolver; @SlingObject private ResourceResolver resourceResolver;
@SlingObject
org.apache.sling.models.annotations.injectorspecific.SlingObject
SlingHttpServletRequest, ResourceResolverもしくはResourceから派生する共通のSlingレベルのオブジェクトを挿入する。
@Model Annotationの引数 adaptables に SlingHttpServletRequest を設定すると、以下すべてがこのAnnotationで使用可能。
- ResourceResolver
- Resource
- SlingHttpServletRequest
- SlingHttpServletResponse
- SlingScriptHelper
@ScriptVariable
org.apache.sling.models.annotations.injectorspecific.ScriptVariable
@ScriptVariable Annotationは、HTL Global Object を変数に挿入する。
一部、上で紹介した @SlingObject と挿入できるオブジェクトが被っているが、こちらの @ScriptVariable の方が挿入できるオブジェクトの種類が多い。
一覧はここには書かないので、上記の「HTL Global Object」のリンクを参照。
@Via
org.apache.sling.models.annotations.Via
@Via Annotationは @Self や @Inject や @ValueMapValue など他のAnnotationとセットで使われ、どういうクラスを経由して挿入するかを指定します。
サンプルコード
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { @Inject @Via("resource") private List<Resource> multifieldValues; }
こうすることによって、multifieldで複数選択した項目を List<Resource> という形で勝手に挿入してくれる。
下記のようなNode構造で保存されているのを勝手に変換してくれるので便利です。
multifieldValues
+-- item0
+-- property0="value0"
+-- item1
+-- property1="value1"
上記は @Inject を使って以下のように書くこともできます。
@Inject(via = "resource") private List<Resource> multifieldValues;
また、以下のような使い方もあります。
@Model(adaptables = { SlingHttpServletRequest.class }) public interface TutorialModelClass { @Inject @Via(value = "jcr:content", type = ChildResource.class) String getPropertyName(); }
この getPropertyName() メソッドは以下のメソッドと同等の動きをします。
public String getPropertyName() { return resource.getChild("jcr:content").getValueMap().get("propertyName", String.class); }
@ValueMapValue
org.apache.sling.models.annotations.injectorspecific.ValueMapValue
@ValueMapValue Annotationは、呼び出し元のコンポーネントのダイアログの値を挿入できます。
サンプルコード
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { // 変数名がdialogのnameプロパティの値が "rootPath" と一致している必要あり @ValueMapValue private String rootPath; public String getRootPath() { return rootPath; } }
@Named
javax.inject.Named
@Named Annotationは、挿入する対象の名前を明示的にします。
サンプルコード
先の @ValueMapValue Annotationで「変数名がdialogのnameプロパティと一致している必要がある」としましたが、この @Name Annotationを使用すれば、変数名を自由にできます。
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { @ValueMapValue @Named("rootPath") private String topPagePath; public String getRootPath() { return topPagePath; } }
@PostConstruct
javax.annotation.PostConstruct
@PostConstruct Annotationは、付与したメソッドを初期化するためのメソッドとして定義します。
サンプルコード
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { protected static final String RESOURCE_TYPE = "myapp/any/sling/resourcetype"; private String toHTL; @PostConstruct protected void init() { toHTL = "print to HTL"; } public String getToHTL() { return toHTL; } }
@ResourcePath
org.apache.sling.models.annotations.injectorspecific.ResourcePath
@ResourcePath AnnotationはResourceにpathの情報を付与するAnnotationです。
以下の引数を与えます。
| 引数名 | 型 | 効果 | 必須 or 任意 |
|---|---|---|---|
| injectionStrategy | InjectionStrategy.class | Sling Modelに挿入されたフィールドが必須かオプションかを決める。デフォルトは必須 | 任意 |
| name | String | プロパティの名前を指定する | 任意 |
| path | String | リソースパスを含むプロパティのパスを指定します | 任意 |
| paths | String[] | リソースの複数のパスを指定します | 任意 |
サンプルコード
@Model( adaptables = { SlingHttpServletRequest.class }, adapters = { TutorialModellClass.class }, resourceType = { TutorialModelClasImpl.RESOURCE_TYPE }, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialModelClassImpl implements TutorialModelClass { @SlingObject @ResourcePath(name = "currentResource") Resource componentResource; @ResourcePath(path = "/content/we-retail/us/en") Resource givenPathResource; @ResourcePath(paths = {"/content/we-retail/us/en", "/content/we-retail/de/de"}) Resource[] givenPathResources; public String getComponentResourcePath() { // Sling Modelsとして呼び出されたコンポーネントのresourceのパスを返す return componentResource.getPath(); } public String getPath() { // "/content/we-retail/us/en"を返す return givenPathResource.getPath(); } public List<String> getPaths() { return Arrays.asList(givenPathResources).stream() .map(Resource::getPath) .collect(Collectors.toList()); } }
最後に
次回は、 @Self , @ChildResource , @RequestAttribute , @OSGiService について解説します!