Fermer

juin 14, 2021

É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.

javacle 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

Adobe - Content for Everyone
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.

Obtenez le guide

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 Set getComponentsResourceTypes() { 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(); }
}
Ensuite, nous naviguons vers en.html et nous voyons que les classes 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.
Ce modèle de délégation présente quelques inconvénients. Tout d'abord, nous devons écrire un code de plomberie monotone pour déléguer le modèle de base. Si la couverture de code est une règle, nous sommes responsables de 21 lignes ! Deuxièmement, il est sujet aux erreurs. L'interface com.adobe.cq.wcm.core.components.models.Page utilise les méthodes par défaut. Vous pouvez omettre n'importe lequel d'entre eux de la classe d'implémentation. Si vous omettez 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

À propos de l'auteur

Juan Ayala est un développeur principal dans la pratique Adobe de Perficient, Inc. , axé sur la plate-forme Adobe Experience et les éléments qui l'entourent.

En savoir plus sur cet auteur






Source link