CDI Interceptor vs Spring Aspect J.

1. Introduzione

Il pattern Interceptor viene generalmente utilizzato per aggiungere nuove funzionalità o logiche trasversali in un'applicazione e ha un solido supporto in un gran numero di librerie.

In questo articolo tratteremo e confronteremo due di queste principali librerie: CDI interceptor e Spring AspectJ.

2. Installazione del progetto CDI Interceptor

CDI è ufficialmente supportato per Jakarta EE ma alcune implementazioni forniscono supporto per utilizzare CDI in ambiente Java SE. La saldatura può essere considerata come un esempio di implementazione CDI supportata in Java SE.

Per poter utilizzare CDI dobbiamo importare la libreria Weld nel nostro POM:

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

La libreria Weld più recente può essere trovata nel repository Maven.

Creiamo ora un semplice intercettore.

3. Presentazione del CDI Interceptor

Per designare le classi che dovevamo intercettare, creiamo il binding dell'interceptor:

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

Dopo aver definito il binding dell'interceptor, dobbiamo definire l'effettiva implementazione dell'interceptor:

@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; } }

Ogni @AroundInvoke metodo prende un javax.interceptor.InvocationContext argomento, restituisce un java.lang.Object , e può lanciare un eccezione .

Quindi, quando annotiamo un metodo con la nuova interfaccia @Audit , auditMethod verrà richiamato per primo e solo allora procederà anche il metodo di destinazione.

4. Applicare CDI Interceptor

Applichiamo l'interceptor creato su alcune logiche di business:

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

Abbiamo creato questo semplice servizio e annotato il metodo che volevamo intercettare con l' annotazione @Audited .

Per abilitare l'interceptor CDI è necessario specificare il nome completo della classe nel file bean.xml , situato nella directory META-INF :

  com.baeldung.interceptor.AuditedInterceptor  

Per convalidare che l'interceptor abbia effettivamente funzionato, eseguiamo ora il seguente 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); } }

In questo rapido test, prima otteniamo il bean SuperService dal contenitore, quindi richiamiamo il metodo di business deliverService su di esso e controlliamo che l'interceptor AuditedInterceptor sia stato effettivamente chiamato convalidando le sue variabili di stato.

Inoltre abbiamo i metodi annotati @Before e @After in cui inizializziamo e spegniamo il contenitore di saldatura rispettivamente.

5. Considerazioni CDI

Possiamo sottolineare i seguenti vantaggi degli intercettori CDI:

  • È una caratteristica standard della specifica Jakarta EE
  • Alcune librerie di implementazioni CDI possono essere utilizzate in Java SE
  • Può essere utilizzato quando il nostro progetto ha gravi limitazioni sulle librerie di terze parti

Gli svantaggi degli intercettori CDI sono i seguenti:

  • Stretto accoppiamento tra classe con logica aziendale e interceptor
  • Difficile vedere quali classi vengono intercettate nel progetto
  • Mancanza di un meccanismo flessibile per applicare gli intercettori a un gruppo di metodi

6. Aspetto PrimaveraJ

Spring supporta un'implementazione simile della funzionalità di intercettazione utilizzando anche la sintassi AspectJ.

First we need to add the following Spring and AspectJ dependencies to POM:

 org.springframework spring-context 5.2.8.RELEASE   org.aspectj aspectjweaver 1.9.2 

The most recent versions of Spring context, aspectjweaver can be found in the Maven repository.

We can now create a simple aspect using AspectJ annotation syntax:

@Aspect public class SpringTestAspect { @Autowired private List accumulator; @Around("execution(* com.baeldung.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; } }

We created an aspect which applies to all the methods of SpringSuperService class – which, for simplicity, looks like this:

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

7. Spring AspectJ Aspect Apply

In order to validate that aspect really applies to the service, let's write the following unit test:

@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")); } }

In this test we inject our service, call the method and check the result.

Here's what the configuration looks like:

@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(); } }

One important aspect here in the @EnableAspectJAutoProxy annotation – which enables support for handling components marked with AspectJ's @Aspect annotation, similar to functionality found in Spring's XML element.

8. Spring AspectJ Considerations

Let's point out a few of the advantages of using Spring AspectJ:

  • Interceptors are decoupled from the business logic
  • Interceptors can benefit from dependency injection
  • Interceptor has all the configuration information in itself
  • Adding new interceptors wouldn't require augmenting existing code
  • Interceptor has flexible mechanism to choose which methods to intercept
  • Can be used without Jakarta EE

And of course a few of the disadvantages:

  • You need to know the AspectJ syntax to develop interceptors
  • La curva di apprendimento per gli intercettori AspectJ è maggiore di quella per gli intercettori CDI

9. CDI Interceptor vs Spring AspectJ

Se il tuo progetto attuale utilizza Spring, considerare Spring AspectJ è una buona scelta.

Se stai usando un application server completo, o il tuo progetto non usa Spring (o altri framework come Google Guice) ed è rigorosamente Jakarta EE, allora non resta altro che scegliere l'interceptor CDI.

10. Conclusione

In questo articolo abbiamo coperto due implementazioni del pattern interceptor: CDI interceptor e Spring AspectJ. Abbiamo coperto vantaggi e svantaggi di ciascuno di essi.

Il codice sorgente per gli esempi di questo articolo può essere trovato nel nostro repository su GitHub.