CDI Interceptor vs Spring AspectJ

1. Inleiding

Het Interceptor-patroon wordt over het algemeen gebruikt om nieuwe, transversale functionaliteit of logica aan een applicatie toe te voegen, en heeft solide ondersteuning in een groot aantal bibliotheken.

In dit artikel behandelen en vergelijken we twee van deze belangrijke bibliotheken: CDI-interceptors en Spring AspectJ.

2. CDI Interceptor Project instellen

CDI wordt officieel ondersteund voor Jakarta EE, maar sommige implementaties bieden ondersteuning voor het gebruik van CDI in een Java SE-omgeving. Weld kan worden beschouwd als een voorbeeld van CDI-implementatie die wordt ondersteund in Java SE.

Om CDI te gebruiken, moeten we de Weld-bibliotheek in onze POM importeren:

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

De meest recente Weld-bibliotheek is te vinden in de Maven-repository.

Laten we nu een eenvoudige interceptor maken.

3. Introductie van de CDI Interceptor

Om klassen aan te duiden die we moesten onderscheppen, laten we de interceptorbinding maken:

@InterceptorBinding @Target ({METHOD, TYPE}) @Retention (RUNTIME) openbaar @interface gecontroleerd {}

Nadat we de interceptor-binding hebben gedefinieerd, moeten we de daadwerkelijke interceptor-implementatie definiëren:

@Audited @Interceptor openbare klasse AuditedInterceptor {openbare statische boolean calledBefore = false; openbare statische boolean calledAfter = false; @AroundInvoke public Object auditMethod (InvocationContext ctx) genereert uitzondering {calledBefore = true; Object resultaat = ctx.proceed (); calledAfter = true; resultaat teruggeven; }}

Elke @AroundInvoke methode duurt een javax.interceptor.InvocationContext argument, geeft een terug java.lang.Object, en kan een Uitzondering.

En dus, wanneer we een methode annoteren met de nieuwe @Audit koppel, auditMethod wordt eerst aangeroepen, en pas daarna gaat de doelmethode ook verder.

4. Pas de CDI Interceptor toe

Laten we de gemaakte interceptor toepassen op een aantal zakelijke logica:

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

We hebben deze eenvoudige service gemaakt en de methode die we wilden onderscheppen geannoteerd met de @Audited annotatie.

Om de CDI-interceptor in te schakelen, moet de volledige klassennaam in het bonen.xml bestand in het META-INF directory:

  com.baeldung.interceptor.AuditedInterceptor 

Om te valideren dat die interceptor inderdaad heeft gewerkt laten we nu de volgende test uitvoeren:

openbare klasse TestInterceptor {Weld weld; WeldContainer-container; @Before public void init () {weld = new Weld (); container = weld.initialize (); } @After openbare leegte shutdown () {weld.shutdown (); } @Test openbare ongeldig gegevenTheService_whenMethodAndInterceptorExecuted_thenOK () {SuperService superService = container.select (SuperService.class) .get (); Stringcode = "123456"; superService.deliverService (code); Assert.assertTrue (AuditedInterceptor.calledBefore); Assert.assertTrue (AuditedInterceptor.calledAfter); }}

In deze snelle test krijgen we eerst de boon SuperService uit de container en roep vervolgens de bedrijfsmethode aan deliverService erop en controleer die interceptor AuditedInterceptor werd eigenlijk aangeroepen door zijn toestandsvariabelen te valideren.

We hebben ook @Voordat en @Na geannoteerde methoden waarin we Weld container respectievelijk initialiseren en afsluiten.

5. CDI-overwegingen

We kunnen wijzen op de volgende voordelen van CDI-interceptors:

  • Het is een standaardfunctie van de Jakarta EE-specificatie
  • Sommige CDI-implementatiesbibliotheken kunnen worden gebruikt in Java SE
  • Kan worden gebruikt wanneer ons project ernstige beperkingen heeft voor bibliotheken van derden

De nadelen van de CDI-interceptors zijn de volgende:

  • Nauwe koppeling tussen klasse met bedrijfslogica en interceptor
  • Het is moeilijk te zien welke klassen in het project worden onderschept
  • Gebrek aan flexibel mechanisme om interceptors toe te passen op een groep methoden

6. VeeraspectJ

Spring ondersteunt een vergelijkbare implementatie van interceptorfunctionaliteit met behulp van de AspectJ-syntaxis.

Eerst moeten we de volgende Spring- en AspectJ-afhankelijkheden aan POM toevoegen:

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

De meest recente versies van Spring context, aspectjweaver, zijn te vinden in de Maven-repository.

We kunnen nu een eenvoudig aspect maken met behulp van de annotatiesyntaxis van AspectJ:

@Aspect openbare klasse SpringTestAspect {@Autowired privélijstaccumulator; @Around ("execution (* com.baeldung.spring.service.SpringSuperService. * (..))") public Object auditMethod (ProceedingsJoinPoint jp) gooit Throwable {String methodName = jp.getSignature (). GetName (); accumulator.add ("Oproep naar" + methodName); Object obj = jp.proceed (); accumulator.add ("Methode succesvol aangeroepen:" + methodName); terugkeer obj; }}

We hebben een aspect gecreëerd dat van toepassing is op alle methoden van SpringSuperService class - die er voor de eenvoud als volgt uitziet:

openbare klasse SpringSuperService {openbare tekenreeks getInfoFromService (tekenreekscode) {retourcode; }}

7. Spring AspectJ Aspect van toepassing

Laten we de volgende unit-test schrijven om te valideren dat dit aspect echt van toepassing is op de service:

@RunWith (SpringRunner.class) @ContextConfiguration (classes = {AppConfig.class}) openbare klasse TestSpringInterceptor {@Autowired SpringSuperService springSuperService; @Autowired privélijstaccumulator; @Test openbare ongeldig gegevenService_whenServiceAndAspectExecuted_thenOk () {String code = "123456"; String resultaat = springSuperService.getInfoFromService (code); Assert.assertThat (accumulator.size (), is (2)); Assert.assertThat (accumulator.get (0), is ("Call to getInfoFromService")); Assert.assertThat (accumulator.get (1), is ("Methode succesvol aangeroepen: getInfoFromService")); }}

In deze test injecteren we onze service, bellen we de methode en controleren het resultaat.

Hier is hoe de configuratie eruit ziet:

@Configuration @EnableAspectJAutoProxy openbare klasse AppConfig {@Bean openbare SpringSuperService springSuperService () {retourneer nieuwe SpringSuperService (); } @Bean openbare SpringTestAspect springTestAspect () {retourneer nieuwe SpringTestAspect (); } @Bean openbare lijst getAccumulator () {retourneer nieuwe ArrayList (); }}

Een belangrijk aspect hier in de @EnableAspectJAutoProxy annotatie - die ondersteuning mogelijk maakt voor het omgaan met componenten die zijn gemarkeerd met AspectJ's @Aspect annotatie, vergelijkbaar met functionaliteit in het XML-element van Spring.

8. Overwegingen voor het voorjaarsaspect

Laten we een paar voordelen noemen van het gebruik van Spring AspectJ:

  • Interceptors zijn losgekoppeld van de bedrijfslogica
  • Interceptoren kunnen profiteren van afhankelijkheidsinjectie
  • Interceptor heeft alle configuratie-informatie op zich
  • Voor het toevoegen van nieuwe interceptors is het niet nodig om bestaande code uit te breiden
  • Interceptor heeft een flexibel mechanisme om te kiezen welke methoden worden onderschept
  • Kan zonder Jakarta EE worden gebruikt

En natuurlijk een paar van de nadelen:

  • U moet de AspectJ-syntaxis kennen om interceptors te ontwikkelen
  • De leercurve voor de AspectJ-interceptors is hoger dan voor de CDI-interceptors

9. CDI Interceptor vs Spring AspectJ

Als uw huidige project Spring gebruikt, is het overwegen van Spring AspectJ een goede keuze.

Als u een volledige applicatieserver gebruikt, of uw project maakt geen gebruik van Spring (of andere frameworks zoals Google Guice) en is strikt Jakarta EE, dan rest er niets anders dan de CDI-interceptor te kiezen.

10. Conclusie

In dit artikel hebben we twee implementaties van het interceptorpatroon behandeld: CDI-interceptor en Spring AspectJ. We hebben elk van hen voor- en nadelen behandeld.

De broncode voor voorbeelden van dit artikel is te vinden in onze repository op GitHub.