Rédaction de code Java dans AEM avec des modèles Sling

J'adore écrire du code. Ce que j'aime le plus, c'est moins d'écrire. Moins de code signifie moins de tests unitaires car vous avez beaucoup moins de couverture à atteindre. Bien organisé, modulaire, court et concis. Comment y parvenez-vous? Bonnes pratiques d'ingénierie logicielle, générateurs de code et exploitation des frameworks et API existants.
Exploitation des frameworks et API
En tant que développeur AEM, vous travaillez au sein d'une plate-forme qui comprend des tonnes de frameworks et d'API OOTB. Consultez la liste des bundles OSGi à l'adresse http: // localhost: 4502 / system / console / bundles . Par exemple, lors d'une récente visite (AEM 6.5.5.0), j'ai remarqué Commons BeanUtils 1.8.2.
Sling Models
L'un des frameworks les plus connus est le Sling Modèles cadre. «Mieux connu» dans la mesure où nous connaissons tous les cas d'utilisation simples. Cependant, peu de gens savent ce qui se passe sous le capot. Assez pour mériter son propre article de blog, c'est certain. Dans le contexte de celui-ci, c’est l’un des cadres qui vous aideront à atteindre notre objectif.
Prenons le cas d’utilisation simple du AEM Granite Multifield . Il semble que tous les projets sur lesquels j'ai travaillé contiennent des tonnes de code pour lire et écrire des données multi-champs. Il y a 3 raisons majeures:
- Dans les premières versions, le multifield ne pouvait stocker que des données simples. Les développeurs ont dû l'étendre et des tonnes d'exemples existaient en ligne. Il s'est transformé en ACS Commons Multifield Extension maintenant obsolète. Et donc, une partie du code que je trouve aujourd'hui est héritée.
- Les développeurs ne sont pas familiers avec les injecteurs et comment les exploiter.
- Parfois, les développeurs copient et collent à partir d'un ancien projet. Horrible, je sais!
Prenons cet exemple de cq: dialog . Il a un texte et plusieurs champs. Le multi-champ est un composite de champs de texte, de chemin et de balise.
Nous pouvons écrire une interface de modèle Sling simple:
@Model (adaptables = {Resource.class}) interface publique MyModel { @ValueMapValue String getText (); @ChildResource (injectionStrategy = InjectionStrategy.OPTIONAL) List- getItems (); @Model (adaptables = Resource.class) Élément d'interface { @ValueMapValue (nom = "jcr: titre") String getTitle (); @ResourcePath Page getPage (); @ValueMapValue Liste
getTags (); } }
Du contenu pour tous
Les entreprises capables de répondre rapidement et systématiquement aux demandes des consommateurs prospèrent à une époque de contenu infini. Découvrez comment créer des expériences fluides pour vos clients omnicanal.
Évitez @Inject . L'injection de valeur se produit au moment de l'exécution. Si vous utilisez @Inject vous faites en sorte que le framework fasse des conjectures supplémentaires. Soyez explicite. Voici les annotations de l'injecteur que j'ai utilisées avec un lien vers le code source. Cela vaut la peine de jeter un œil pour mieux comprendre le fonctionnement de l'injection.
- @ValueMapValue – Celui que vous utiliserez le plus, injecter des propriétés de ressources simples.
- @ChildResource – Les données sont enregistrées dans un hiérarchie des ressources que le cadre adaptera davantage au modèle Item .
- @ResourcePath – Cela augmentera la valeur de la propriété, la résoudra en une ressource, puis l'adaptera à un Page .
Et voilà! La charpente fournit toute la plomberie. Aucune itération enfant, classes DTO, résolution de ressources ou adaptations d'aucune sorte. Mais attendez… les balises me dérangent vraiment. J'ai pu mapper un chemin de page vers un objet Page réel. Ne puis-je pas mapper ces identifiants de balise à un objet Tag ?
La réponse courte est non. Les balises sont un concept AEM et c'est Sling Models. Vous ne pouvez même pas résoudre un identifiant de balise via le résolveur de ressources. Vous devez utiliser le TagManager . Il convient de noter qu’il existe d’autres implémentations d’injecteurs open source. Prenons par exemple ce TagInjector du projet I CF Olson AEM Library .
My Spidey Sense is Tingling
Les implémentations open-source sont très bien et tout; mais il y a même les injecteurs d'ACS Commons . Mais mon sens de l'araignée me dit que je peux toujours utiliser les modèles Sling et obtenir très peu de code. Défi accepté! Je suis allé prendre une tasse de café et j'ai passé un peu de temps avec mes chiens dans la cour. J'ai réfléchi un peu à l'injection, et c'est ce que j'ai proposé.
Les identifiants de balises sont enregistrés dans une propriété String [] de la ressource item. Nous savons que dans Sling, même les propriétés sont des ressources qui peuvent être adaptées à leur type de base. Par exemple:
Resource tags = resource.getChild ("items / item0 / tags"); String [] value = tags.adaptTo (String [] .class);
J'ai donc changé le type de retour sur getTags () en mon propre type ItemTags et changé l'annotation d'injecteur à @ChildResource .
@Model (adaptables = {Resource.class}) interface publique MyModel { @ValueMapValue String getText (); @ChildResource (injectionStrategy = InjectionStrategy.OPTIONAL) List- getItems (); @Model (adaptables = Resource.class) Élément d'interface { @ValueMapValue (nom = "jcr: titre") String getTitle (); @ResourcePath Page getPage (); @ChildResource ItemTags getTags (); } interface ItemTags étend Iterable
{ } @Model (adaptables = Resource.class, adaptateurs = ItemTags.class) la classe finale ItemTagsImpl implémente ItemTags { balises de la liste finale privée ; @Injecter public ItemTagsImpl ( @Soi @Via ("resourceResolver") tagManager final TagManager, @Soi final String [] ids) { this.tags = Arrays.stream (identifiants) .map (tagManager :: résoudre) .filter (Objets :: nonNull) .collect (Collectors.toUnmodifiableList ()); } @Passer outre public Iterator iterator () { return this.tags.iterator (); } @Passer outre public void forEach (action finale du consommateur super Tag>) { this.tags.forEach (action); } @Passer outre public Spliterator spliterator () { retourne this.tags.spliterator (); } } }
Le reste de mes décisions consistait principalement à faciliter les tests unitaires.
- J'ai séparé les ImageTags en interface et implémentation parce que les interfaces facilitent les tests unitaires.
- J'ai choisi l'injection de constructeur parce qu'elle est plus simple de renouveler la classe dans un test unitaire que de configurer des injections simulées.
- La seule couverture de code dont je suis responsable est maintenant de 4 lignes de code (le constructeur et les fonctions getter)
De plus, l'objet est immuable et itérable pour plus de commodité. Notez l'utilisation de l'annotation @Self et @Via dans l'injection du constructeur. Ils indiquent au framework de prendre la chose à adapter, la propriété tags resource.getChild («items / item0 / tags»), et de l'adapter à un TagManager via le résolveur de ressources. Et aussi l'adapter à un tableau d'identifiants au moyen d'un simple adaptTo (String [] .class) . Bien organisé, modulaire, court et concis.
Conclusion: n’oubliez pas de vérifier la plomberie
Cela vaut la peine de parcourir la documentation Sling Models et de la connaître. Il fournit beaucoup de plomberie. Même si vous êtes obligé d'implémenter vos propres injecteurs, c'est un excellent moyen de garder votre code modulaire et propre. Dans le prochain article, je traiterai des générateurs de code.
J'espère que vous avez trouvé ce billet de blog utile. Pour toute question supplémentaire, veuillez commenter ci-dessous et n'oubliez pas de consulter nos autres blogs Adobe .
Source link