CDI Interceptor vs Spring AspectJ

CDI Interceptor vs Spring AspectJ

1. introduction

Le modèle Interceptor est généralement utilisé pour ajouter de nouvelles fonctionnalités ou logiques transversales dans une application et prend en charge de manière solide un grand nombre de bibliothèques.

Dans cet article, nous allons couvrir et comparer deux de ces bibliothèques majeures: les intercepteurs CDI et Spring AspectJ.

2. Configuration du projet d'intercepteur CDI

CDI est officiellement pris en charge pour Java EE, mais certaines implémentations permettent d’utiliser CDI dans un environnement Java SE. Weld peut être considéré comme un exemple d'implémentation CDI prise en charge dans Java SE.

Pour utiliser CDI, nous devons importer la bibliothèque Weld dans notre POM:


    org.jboss.weld.se
    weld-se-core
    2.3.5.Final

La bibliothèque de soudure la plus récente se trouve dans le référentiel deMaven.

Créons maintenant un simple intercepteur.

3. Présentation de l'intercepteur CDI

Afin de désigner les classes que nous devions intercepter, créons la liaison d'intercepteur:

@InterceptorBinding
@Target( { METHOD, TYPE } )
@Retention( RUNTIME )
public @interface Audited {
}

Après avoir défini la liaison d'intercepteur, nous devons définir l'implémentation réelle de l'intercepteur:

@Audited
@Interceptor
public class AuditedInterceptor {
    public static boolean calledBefore = false;
    public static boolean calledAfter = false;

    @AroundInvoke
    public Object auditMethod(InvocationContext ctx) throws Exception {
        calledBefore = true;
        Object result = ctx.proceed();
        calledAfter = true;
        return result;
    }
}

Chaque méthode@AroundInvoke prend un argumentjavax.interceptor.InvocationContext, renvoie unjava.lang.Object et peut lancer unException.

Ainsi, lorsque nous annotons une méthode avec la nouvelle interface@Audit,auditMethod sera appelé en premier, et alors seulement la méthode cible continuera également.

4. Appliquer l'intercepteur CDI

Appliquons l'intercepteur créé à une logique métier:

public class SuperService {
    @Audited
    public String deliverService(String uid) {
        return uid;
    }
}

Nous avons créé ce service simple et annoté la méthode que nous voulions intercepter avec l'annotation@Audited.

Pour activer l'intercepteur CDI, il faut spécifier le nom complet de la classe dans le fichierbeans.xml, situé dans le répertoireMETA-INF:


    
        com.example.interceptor.AuditedInterceptor
    

Pour valider que cet intercepteur a bien fonctionnélet’s now run the following test:

public class TestInterceptor {
    Weld weld;
    WeldContainer container;

    @Before
    public void init() {
        weld = new Weld();
        container = weld.initialize();
    }

    @After
    public void shutdown() {
        weld.shutdown();
    }

    @Test
    public void givenTheService_whenMethodAndInterceptorExecuted_thenOK() {
        SuperService superService = container.select(SuperService.class).get();
        String code = "123456";
        superService.deliverService(code);

        Assert.assertTrue(AuditedInterceptor.calledBefore);
        Assert.assertTrue(AuditedInterceptor.calledAfter);
    }
}

Dans ce test rapide, nous obtenons d'abord le beanSuperService du conteneur, puis invoquons la méthode métierdeliverService dessus et vérifions que l'intercepteurAuditedInterceptor a été réellement appelé en validant ses variables d'état.

Nous avons également les méthodes annotées@Before et@After dans lesquelles nous initialisons et arrêtons respectivement le conteneur de soudure.

5. Considérations CDI

Nous pouvons souligner les avantages suivants des intercepteurs CDI:

  • C'est une fonctionnalité standard de la spécification Java EE

  • Certaines bibliothèques d’implémentation de CDI peuvent être utilisées dans Java SE

  • Peut être utilisé lorsque notre projet a des limitations sévères sur les bibliothèques tierces

Les inconvénients des intercepteurs CDI sont les suivants:

  • Couplage étroit entre la classe avec la logique métier et l'intercepteur

  • Difficile de voir quelles classes sont interceptées dans le projet

  • Absence de mécanisme flexible permettant d'appliquer des intercepteurs à un groupe de méthodes

6. Aspect du printempsJ

Spring prend en charge une implémentation similaire de la fonctionnalité d'intercepteur utilisant également la syntaxe AspectJ.

Nous devons d’abord ajouter les dépendances Spring et AspectJ suivantes à POM:


    org.springframework
    spring-context
    4.3.1.RELEASE


    org.aspectj
    aspectjweaver
    1.8.9

Les versions les plus récentes deSpring context,aspectjweaver se trouvent dans le référentiel Maven.

Nous pouvons maintenant créer un aspect simple en utilisant la syntaxe d'annotation AspectJ:

@Aspect
public class SpringTestAspect {
    @Autowired
    private List accumulator;

    @Around("execution(* com.example.spring.service.SpringSuperService.*(..))")
    public Object auditMethod(ProceedingJoinPoint jp) throws Throwable {
        String methodName = jp.getSignature().getName();
        accumulator.add("Call to " + methodName);
        Object obj = jp.proceed();
        accumulator.add("Method called successfully: " + methodName);
        return obj;
    }
}

Nous avons créé un aspect qui s'applique à toutes les méthodes de la classeSpringSuperService - qui, pour simplifier, ressemble à ceci:

public class SpringSuperService {
    public String getInfoFromService(String code) {
        return code;
    }
}

7. Spring AspectJ Aspect Appliquer

Afin de valider que cet aspect s'applique vraiment au service, écrivons le test unitaire suivant:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = { AppConfig.class })
public class TestSpringInterceptor {
    @Autowired
    SpringSuperService springSuperService;

    @Autowired
    private List accumulator;

    @Test
    public void givenService_whenServiceAndAspectExecuted_thenOk() {
        String code = "123456";
        String result = springSuperService.getInfoFromService(code);

        Assert.assertThat(accumulator.size(), is(2));
        Assert.assertThat(accumulator.get(0), is("Call to getInfoFromService"));
        Assert.assertThat(accumulator.get(1), is("Method called successfully: getInfoFromService"));
    }
}

Dans ce test, nous injectons notre service, appelons la méthode et vérifions le résultat.

Voici à quoi ressemble la configuration:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public SpringSuperService springSuperService() {
        return new SpringSuperService();
    }

    @Bean
    public SpringTestAspect springTestAspect() {
        return new SpringTestAspect();
    }

    @Bean
    public List getAccumulator() {
        return new ArrayList();
    }
}

Un aspect important ici dans l'annotation@EnableAspectJAutoProxy - qui permet la prise en charge de la gestion des composants marqués avec l'annotation@Aspect d'AspectJ, similaire à la fonctionnalité trouvée dans l'élément XML de Spring.

8. Aspect du printempsJ Considérations

Soulignons quelques-uns des avantages de l'utilisation de Spring AspectJ:

  • Les intercepteurs sont découplés de la logique métier

  • Les intercepteurs peuvent bénéficier d'une injection de dépendance

  • Interceptor a toutes les informations de configuration en soi

  • L'ajout de nouveaux intercepteurs ne nécessiterait pas d'augmenter le code existant

  • Interceptor dispose d'un mécanisme flexible pour choisir les méthodes à intercepter

  • Peut être utilisé sans Java EE

Et bien sûr quelques inconvénients:

  • Vous devez connaître la syntaxe AspectJ pour développer des intercepteurs

  • La courbe d'apprentissage pour les intercepteurs AspectJ est plus élevée que pour les intercepteurs CDI

9. CDI Interceptor VS Spring AspectJ

Si votre projet actuel utilise Spring, considérer Spring AspectJ est un bon choix.

Si vous utilisez un serveur d’applications à part entière, ou si votre projet n’utilise pas Spring (ou d’autres frameworks comme Google Guice) et est strictement Java EE, il ne reste plus qu’à choisir l’intercepteur CDI.

10. Conclusion

Dans cet article, nous avons couvert deux implémentations du modèle d'intercepteur: l'intercepteur CDI et Spring AspectJ. Nous avons couvert les avantages et les inconvénients de chacun d’eux.

Le code source des exemples de cet article se trouve dans notre référentiel surGitHub.