Écrire moins de code Java dans AEM avec Sling Models et Lombok

AEM est une plate-forme robuste pleine d'API et de frameworks utiles disponibles à notre disposition. Comprendre ce qu'il y a dans la boîte nous aidera à écrire moins de code. Dans mon article de blog précédentj'ai couvert l'un des frameworks les plus utilisés, Sling Models. De plus, j'ai montré un exemple réel avec des champs multiples. Pour cet article de blog, nous continuerons à tirer parti des modèles Sling avec l'aide supplémentaire d'un générateur de code.
Générateurs de code
Les générateurs de code nous évitent d'avoir à écrire du code de plomberie Java monotone. Des choses comme bean getters et settersloggers, equals()toString() et hashCode(). Nous devons nous libérer d'avoir à écrire et à entretenir cette plomberie. Ce faisant, nous pouvons nous concentrer sur la logique métier de haut niveau.
Certains des générateurs les plus connus sont ImmutablesAutoValue et Lombok. Je ne vais pas entrer dans les détails de chacun. Qu'il suffise de dire qu'après quelques années et quelques projets, ma préférence personnelle est Lombok. Comme les deux autres, il peut générer des objets de valeur. Mais il fait bien plus que cela.
javac
le compilateur Java, exécute des générateurs de code. Comme toutes les choses en Java, il existe un JSR. Dans ce cas JSR 269 : API de traitement des annotations enfichables. Puisque nous utilisons Maven, il nous suffit d'ajouter la dépendance Maven
org.projectlombok lombok 1.18.20 provided
Contenu pour tous
Les entreprises capables de répondre rapidement et de manière cohérente aux demandes des consommateurs prospèrent à l'ère du contenu infini. Découvrez comment créer des expériences fluides pour vos clients omnicanaux.
Comme par magie, toutes vos classes annotées Lombok seront augmentées avec le code généré. Eh bien… pas magique. Javac trouvera et chargera META-INF/services/javax.annotation.processing.Processor
. Ce fichier réside dans lombok.jar
et pointe vers le processeur d'annotation approprié. Vous pouvez lire une explication plus détaillée ici. Il est si fluide que la première fois que j'ai démoli un projet construit avec Lombok, je n'étais même pas conscient de sa présence. Pas avant de tomber sur une classe annotée. C'est du côté du compilateur. Côté IDE, la dernière version d'IntelliJ est déjà compatible avec Lombok. Eclipse nécessite une certaine configuration.
Un exemple réel
À un moment donné, chaque développeur AEM a dû ajouter des classes à la balise . Cela semble simple, non ? Nous allons ajouter une contrainte : étendre correctement le WCM Core Page Template Component. La réaction instinctive serait d'écraser le fichier page.html où se trouve la balise
. J'ai bien dit « extension de propriété » ? Ce fichier contient beaucoup de choses pour prendre en charge la plate-forme et les fonctionnalités d'Adobe. Si nous écrasons, nous deviendrions alors responsables de le maintenir à jour entre les mises à jour.
Les classes viennent du modèle de page com.adobe.cq.wcm.core.components.models.Page . La bonne façon d'étendre le modèle de base consiste à utiliser le ResourceSuperTypeun type de fournisseur Via. Ici est un exemple plus détaillé de la façon d'étendre les composants de base.
Lancez un nouveau projet archétype :
mvn -B archétype:générer -D archetypeGroupId=com.adobe.aem -D archetypeArtifactId=aem-project-archetype -D archetypeVersion=26 -D appTitle="Mon site" -D appId="monsite" -D groupId="com.monsite" -D aemVersion=6.5.5
Et la première classe que nous allons créer est com.mysite.core.models.MyPageModel
@Model(adaptables = { SlingHttpServletRequest.class }, adapters = { Page.class , ContainerExporter.class }, resourceType = "monsite/composants/page") @Exporter(nom = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) La classe finale publique MyPageModel implémente Page, ContainerExporter { journal final statique privé de l'enregistreur = LoggerFactory.getLogger(MyPageModel.class); @Soi @Via(type = ResourceSuperType.class) délégué Page privée; @Passer outre chaîne publique getCssClassNames() { log.info("c'est le seul changement pertinent"); return String.join(" ", "foo", "bar", this.delegate.getCssClassNames()); } /** Tout en dessous, c'est la plomberie. */ @Passer outre public String getLanguage() { return this.delegate.getLanguage(); } @Passer outre Calendrier public getLastModifiedDate() { return this.delegate.getLastModifiedDate(); } @Passer outre public String[] getKeywords() { return this.delegate.getKeywords(); } @Passer outre public String getDesignPath() { return this.delegate.getDesignPath(); } @Passer outre public String getStaticDesignPath() { return this.delegate.getStaticDesignPath(); } @Passer outre public String getTitle() { return this.delegate.getTitle(); } @Passer outre public String[] getClientLibCategories() { return this.delegate.getClientLibCategories(); } @Passer outre public String[] getClientLibCategoriesJsBody() { return this.delegate.getClientLibCategoriesJsBody(); } @Passer outre public String[] getClientLibCategoriesJsHead() { return this.delegate.getClientLibCategoriesJsHead(); } @Passer outre public String getTemplateName() { return this.delegate.getTemplateName(); } @Passer outre public String getAppResourcesPath() { return this.delegate.getAppResourcesPath(); } @Passer outre public NavigationItem getRedirectTarget() { return this.delegate.getRedirectTarget(); } @Passer outre public boolean hasCloudconfigSupport() { return this.delegate.hasCloudconfigSupport(); } @Passer outre public SetgetComponentsResourceTypes() { return this.delegate.getComponentsResourceTypes(); } @Passer outre public String[] getExportedItemsOrder() { return this.delegate.getExportedItemsOrder(); } @Passer outre public Map getExportedItems() { return this.delegate.getExportedItems(); } @Passer outre public String getExportedType() { return this.delegate.getExportedType(); } @Passer outre public String getMainContentSelector() { return this.delegate.getMainContentSelector(); } @Passer outre public List getHtmlPageItems() { return this.delegate.getHtmlPageItems(); } @Passer outre public String getId() { return this.delegate.getId(); } @Passer outre public ComponentData getData() { return this.delegate.getData(); } }
sont maintenant présentes. En plus de cela, nous n'avons cassé aucune fonctionnalité prête à l'emploi comme le modèle JSON de la page.getData()
vous avez rompu l'intégration de la couche de données.Getting Rid of the Boilerplate Code
/** enregistreur statique généré automatiquement. */ @Slf4j @Model(adaptables = { SlingHttpServletRequest.class }, adaptateurs = { Page.class, ContainerExporter.class }, resourceType = "monsite/composants/page") @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) La classe finale publique MyPageModel implémente Page, ContainerExporter { /** getter généré automatiquement. */ @Gettre chaîne privée cssClassNames; /** délègue le modèle de page principal avec des exclusions. */ @Delegate(excludes = MyDelegateExclusions.class) @Soi @Via(type = ResourceSuperType.class) délégué Page privée; @PostConstruct annulation publique active () { log.trace("activation des composants"); this.cssClassNames = String.join(" ", "lombok", "foo", "bar", this.delegate.getCssClassNames()); } /** signatures exclues de la délégation. */ interface privée MyDelegateExclusions { /** nous ne voulons pas que cela soit délégué. */ Chaîne getCssClassNames(); } }
Et le code (ou son absence) parle à peu près de lui-même. Nous nous sommes débarrassés de l'omniprésent logger en ajoutant @Slf4J au niveau de la classe. Pourtant, notre log
statique privé est toujours disponible. Nous avons annoté cssClassNames
avec @Getter implémentant ainsi Page.getCssClassNames()
. Et bien sûr, nous nous sommes débarrassés de toutes les méthodes déléguées en annotant le champ delegate
avec @Delegate.
Ce que nous avons appris
Pour commencer, j'espère vous avez appris qu'il existe une bonne façon d'étendre les composants et les modèles de base. Deuxièmement, nous avons appris que les modèles Sling et les générateurs de code ne sont pas exclusifs. Les générateurs de code peuvent fonctionner en conjonction avec les modèles Sling. Vous pouvez également les exploiter sur vos services OSGi et n'importe quelle classe Java au sein de votre application. Jetez un œil aux fonctionnalités stables de Lombok, puis aux expérimentales
Source link