Intro à AspectJ

1. Introduction

Cet article est une introduction rapide et pratique à AspectJ.

Tout d’abord, nous montrerons comment activer la programmation orientée aspect, puis nous nous concentrerons sur la différence entre le temps de compilation, le post-compilation et le tissage au moment du chargement.

Commençons par une brève introduction à la programmation orientée aspect (AOP) et aux bases de AspectJ.

2. Vue d’ensemble

AOP est un paradigme de programmation qui vise à augmenter la modularité en permettant de séparer les problèmes transversaux. Pour ce faire, il ajoute un comportement supplémentaire au code existant sans modification du code lui-même. Au lieu de cela, nous déclarons séparément le code à modifier.

AspectJ met en œuvre à la fois les préoccupations et le recoupement des préoccupations transversales à l’aide d’extensions du langage de programmation Java.

3. Dépendances Maven

AspectJ propose différentes bibliothèques en fonction de son utilisation. Les dépendances Maven se trouvent dans le groupe org.aspectj dans le référentiel Maven Central.

Dans cet article, nous nous intéressons plus particulièrement aux dépendances nécessaires à la création d’aspects et à l’utilisation de Weaver à l’aide des tisserands de compilation, de post-compilation et de chargement.

3.1. AspectJ Runtime

Lors de l’exécution d’un programme AspectJ, le chemin d’accès aux classes doit contenir les classes et les aspects ainsi que la bibliothèque d’exécution AspectJ aspectjrt.jar:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>

3.2. AspectJWeaver

Outre la dépendance d’exécution AspectJ, nous devrons également inclure le fichier aspectjweaver.jar pour donner des conseils à la classe Java au moment du chargement:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

La dépendance est également disponible sur Maven Central .

4. Création d’aspect

AspectJ fournit une implémentation de AOP et a trois concepts de base:

  • Joint Point

  • Pointcut

  • Conseil

Nous allons démontrer ces concepts en créant un programme simple pour valider le solde d’un compte d’utilisateur.

Commençons par créer une classe Account avec un solde donné et une méthode de retrait:

public class Account {
    int balance = 20;

    public boolean withdraw(int amount) {
        if (balance < amount) {
            return false;
        }
        balance = balance - amount;
        return true;
    }
}

Nous allons créer un fichier AccountAspect.aj pour consigner les informations de compte et valider le solde du compte (notez que les fichiers AspectJ se terminent par une extension « .aj »):

public aspect AccountAspect {
    final int MIN__BALANCE = 10;

    pointcut callWithDraw(int amount, Account acc) :
     call(boolean Account.withdraw(int)) && args(amount) && target(acc);

    before(int amount, Account acc) : callWithDraw(amount, acc) {
    }

    boolean around(int amount, Account acc) :
      callWithDraw(amount, acc) {
        if (acc.balance < amount) {
            return false;
        }
        return proceed(amount, acc);
    }

    after(int amount, Account balance) : callWithDraw(amount, balance) {
    }
}

Comme nous pouvons le constater, nous avons ajouté un pointcut à la méthode retirée et créé trois vises qui font référence au pointcut défini.

Afin de comprendre ce qui suit, nous introduisons les définitions suivantes:

  • Aspect : Une modularisation d’une préoccupation qui touche plusieurs

objets. Chaque aspect se concentre sur une fonctionnalité transversale spécifique Joint point ** : Un point lors de l’exécution d’un script, tel que le

exécution d’une méthode ou d’un accès à une propriété Conseil ** : Action entreprise par un aspect à un point de jonction particulier

  • Pointcut : une expression régulière qui correspond aux points de jointure. Un conseil

est associé à une expression de pointpoint et s’exécute à tout point de jointure correspondant au pointcut

Pour plus de détails sur ces concepts et leur sémantique spécifique, nous pouvons consulter le lien https://eclipse.org/aspectj/doc/next/progguide/semantics-pointcuts.html ]

Ensuite, nous devons intégrer les aspects dans notre code. Les sections ci-dessous traitent de trois types de tissage différents: le tissage au moment de la compilation, le tissage après la compilation et le tissage au chargement dans AspectJ.

5. Tissage à la compilation

L’approche la plus simple du tissage est le tissage à la compilation. Lorsque nous avons à la fois le code source de l’aspect et le code dans lequel nous utilisons des aspects, le compilateur AspectJ compilera à partir de la source et produira un fichier de classe tissé en sortie. Ensuite, lors de l’exécution de votre code, la classe de sortie du processus de tissage est chargée dans la machine virtuelle Java en tant que classe Java normale.

Nous pouvons télécharger les AspectJ Development Tools car ils incluent un compilateur AspectJ fourni. L’une des fonctionnalités les plus importantes d’AJDT est un outil permettant de visualiser les problèmes transversaux, ce qui est utile pour le débogage d’une spécification par coupure de point. Nous pouvons visualiser l’effet combiné avant même que le code ne soit déployé.

Nous utilisons le plugin AspectJ Maven de Mojo pour intégrer des aspects AspectJ dans nos classes à l’aide du compilateur AspectJ.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <Xlint>ignore</Xlint>
        <encoding>UTF-8 </encoding>
    </configuration>
    <executions>
        <execution>
            <goals>
                <!-- use this goal to weave all your main classes -->
                <goal>compile</goal>
                <!-- use this goal to weave all your test classes -->
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Pour plus de détails sur la référence aux options du compilateur AspectJ, nous pouvons consulter le http://www.mojohaus.org/aspectj-maven-plugin/ajc reference/standard opts.html[link].

Ajoutons quelques cas de test pour notre classe Account:

public class AccountTest {
    private Account account;

    @Before
    public void before() {
        account = new Account();
    }

    @Test
    public void given20AndMin10__whenWithdraw5__thenSuccess() {
        assertTrue(account.withdraw(5));
    }

    @Test
    public void given20AndMin10__whenWithdraw100__thenFail() {
        assertFalse(account.withdraw(100));
    }
}

Lorsque nous exécutons les scénarios de test, le texte ci-dessous qui apparaît dans la console signifie que nous avons réussi à tisser le code source:

----[INFO]Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20)
advised by around advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:18(from AccountAspect.aj))
[INFO]Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20)
advised by before advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:13(from AccountAspect.aj))
[INFO]Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20)
advised by after advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:26(from AccountAspect.aj))

2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Balance before withdrawal: 20 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Withdraw ammout: 5 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Balance after withdrawal : 15 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Balance before withdrawal: 20 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Withdraw ammout: 100 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Withdrawal Rejected! 2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.AccountAspect - Balance after withdrawal : 20

===  **  6. Tissage post-compilation **

Le tissage post-compilation (également appelé parfois tissage binaire) est utilisé pour tisser des fichiers de classe et JAR existants. Comme pour le tissage au moment de la compilation, les aspects utilisés pour le tissage peuvent être sous forme source ou binaire, et peuvent eux-mêmes être tissés par des aspects.

Pour faire cela avec le plugin AspectJ Maven de Mojo, nous devons configurer tous les fichiers JAR que nous aimerions tisser dans la configuration du plugin:

[source,xml,gutter:,true]

<configuration> <weaveDependencies> <weaveDependency> <groupId>org.agroup</groupId> <artifactId>to-weave</artifactId> </weaveDependency> <weaveDependency> <groupId>org.anothergroup</groupId> <artifactId>gen</artifactId> </weaveDependency> </weaveDependencies> </configuration>

Les fichiers JAR contenant les classes à tisser doivent être répertoriés sous la forme «<dependencies/>» dans le projet Maven et sous la forme «<weaveDependencies/>» dans la «<configuration>» du plugin AspectJ Maven.

===  **  7. Tissage à la charge **

Le tissage au moment du chargement est simplement un tissage binaire différé jusqu'au moment où un chargeur de classe charge un fichier de classe et définit la classe sur la machine virtuelle Java.

Pour cela, un ou plusieurs «chargeurs de classe de tissage» sont nécessaires. Celles-ci sont fournies explicitement par l'environnement d'exécution ou activées à l'aide d'un «agent de tissage».

====  **  7.1. Activation du tissage au temps de chargement **

Le tissage au moment du chargement AspectJ peut être activé à l'aide de l'agent AspectJ qui peut être impliqué dans le processus de chargement des classes et tisser tous les types avant qu'ils ne soient définis dans la machine virtuelle. Nous spécifions l'option __javaagent__ sur la machine virtuelle Java __`-javaagent: pathto/aspectjweaver.jar`__ ou à l'aide du plug-in Maven pour configurer le __javaagent__:

[source,xml,gutter:,true]

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.10</version> <configuration> <argLine> -javaagent:"${settings.localRepository}"/org/aspectj/ aspectjweaver/${aspectj.version}/ aspectjweaver-${aspectj.version}.jar </argLine> <useSystemClassLoader>true</useSystemClassLoader> <forkMode>always</forkMode> </configuration> </plugin>

====  **  7.2. Configuration Weaver **

L’agent de tissage au moment du chargement de AspectJ est configuré à l’aide des fichiers __aop.xml__. Il recherche un ou plusieurs fichiers __aop.xml__ sur le chemin de classe dans le répertoire __META-INF__ et agrège le contenu pour déterminer la configuration du tisserand.

Un fichier __aop.xml__ contient deux sections clés:

**  **  Aspects ** : définit un ou plusieurs aspects du tisserand et des contrôles

quels aspects doivent être utilisés dans le processus de tissage. Les __spects__
l'élément peut éventuellement contenir un ou plusieurs __include__ et __exclude__
éléments (par défaut, tous les aspects définis sont utilisés pour le tissage)
**  **  Weaver ** : définit les options du tisserand pour le tisserand et spécifie le jeu

des types qui devraient être tissés. Si aucun élément include n'est spécifié, tous les types visibles par le tisserand seront tissés

Configurons un aspect du tisserand:

[source,xml,gutter:,true]

<aspectj> <aspects> <aspect name="com.baeldung.aspectj.AccountAspect"/> <weaver options="-verbose -showWeaveInfo"> <include within="com.baeldung.aspectj.** "/> </weaver> </aspects> </aspectj>

Comme nous pouvons le voir, nous avons configuré un aspect qui pointe sur le __AccountAspect__, et seul le code source du paquet __com.baeldung.aspectj__ sera tissé par AspectJ.

===  **  8. Aspects annotés **

En plus du style de déclaration d'aspect basé sur le code AspectJ, AspectJ 5 prend également en charge un style de déclaration d'aspect basé sur des annotations. Nous appelons de manière informelle l'ensemble d'annotations prenant en charge ce style de développement les annotations «__ @ AspectJ__».

Créons une annotation:

[source,java,gutter:,true]

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Secured { public boolean isLocked() default false; }

Nous utilisons l'annotation __ @ Secured__ pour activer ou désactiver une méthode:

[source,java,gutter:,true]

public class SecuredMethod {

@Secured(isLocked = true)
public void lockedMethod() {
}
    @Secured(isLocked = false)
    public void unlockedMethod() {
    }
}
Ensuite, nous ajoutons un aspect en utilisant le style d'annotation AspectJ et vérifions l'autorisation en fonction de l'attribut de l'annotation @Secured:

[source,java,gutter:,true]

@Aspect public class SecuredMethodAspect { @Pointcut("@annotation(secured)") public void callAt(Secured secured) { }

    @Around("callAt(secured)")
    public Object around(ProceedingJoinPoint pjp,
      Secured secured) throws Throwable {
        return secured.isLocked() ? null : pjp.proceed();
    }
}
Pour plus de détails sur le style d'annotation AspectJ, nous pouvons consulter le lien suivant://eclipse.org/aspectj/doc/released/adk15notebook/annotations-aspectmembers.html[lien].

Ensuite, nous tissons notre classe et notre aspect à l’aide de weaver au moment du chargement et plaçons __aop.xml__ dans le dossier __META-INF__:

[source,xml,gutter:,true]

<aspectj> <aspects> <aspect name="com.baeldung.aspectj.SecuredMethodAspect"/> <weaver options="-verbose -showWeaveInfo"> <include within="com.baeldung.aspectj.** "/> </weaver> </aspects> </aspectj>

Enfin, nous ajoutons un test unitaire et vérifions le résultat:

[source,java,gutter:,true]

@Test public void testMethod() throws Exception { SecuredMethod service = new SecuredMethod(); service.unlockedMethod(); service.lockedMethod(); }

Lorsque nous exécutons les scénarios de test, nous pouvons vérifier la sortie de la console pour nous assurer que nous avons correctement tissé notre aspect et notre classe dans le code source:

[source,java,gutter:,true]

----[INFO]Join point 'method-call
(void com.baeldung.aspectj.SecuredMethod.unlockedMethod())'
in Type 'com.baeldung.aspectj.test.SecuredMethodTest'
(SecuredMethodTest.java:11)
advised by around advice from 'com.baeldung.aspectj.SecuredMethodAspect'
(SecuredMethodAspect.class(from SecuredMethodAspect.java))

2016-11-15 22:53:51[main]INFO com.baeldung.aspectj.SecuredMethod
- unlockedMethod
2016-11-15 22:53:51[main]INFO c.b.aspectj.SecuredMethodAspect -
public void com.baeldung.aspectj.SecuredMethod.lockedMethod() is locked

9. Conclusion

Dans cet article, nous avons abordé les concepts d’introduction à AspectJ. Pour plus de détails, vous pouvez consulter la page d’accueil AspectJ.

Vous pouvez trouver le code source de cet article over sur GitHub .