CDI Interceptor срещу Spring AspectJ

1. Въведение

Моделът Interceptor обикновено се използва за добавяне на нова, многостранна функционалност или логика в приложение и има солидна поддръжка в голям брой библиотеки.

В тази статия ще обхванем и сравним две от тези основни библиотеки: CDI прехващачи и Spring AspectJ.

2. Настройка на CDI Interceptor Project

CDI се поддържа официално за Джакарта EE, но някои изпълнения осигуряват поддръжка за използване на CDI в Java SE среда. Weld може да се разглежда като един пример за изпълнение на CDI, който се поддържа в Java SE.

За да използваме CDI, трябва да импортираме Weld библиотеката в нашия POM:

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

Най-новата библиотека на Weld може да бъде намерена в хранилището на Maven.

Нека сега създадем обикновен прихващач.

3. Представяме ви CDI Interceptor

За да определим класове, които трябваше да прихванем, нека създадем обвързване на прехващача:

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

След като дефинирахме свързването на прехващача, трябва да определим действителното изпълнение на прехващача:

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

Всеки метод @AroundInvoke приема аргумент javax.interceptor.InvocationContext , връща java.lang.Object и може да хвърли изключение .

И така, когато коментираме метод с новия интерфейс @Audit , първо ще се извика auditMethod и едва след това ще продължи и целевият метод.

4. Приложете CDI Interceptor

Нека приложим създадения прихващач на някаква бизнес логика:

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

Създадохме тази проста услуга и коментирахме метода, който искахме да прихване, с анотацията @Audited .

За да активирате CDI прехващача, трябва да посочите пълното име на класа във файла beans.xml , намиращ се в директорията META-INF :

  com.baeldung.interceptor.AuditedInterceptor  

За да потвърдим, че прехващачът наистина е работил, нека сега изпълним следния тест :

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

В този бърз тест първо получаваме боб SuperService от контейнера, след това извикваме бизнес метод DeliveryService върху него и проверяваме дали прехващачът AuditedInterceptor всъщност е бил извикан чрез валидиране на неговите променливи на състоянието.

Също така имаме @Before и @After коментирани методи, в които инициализираме и изключваме Weld контейнера съответно.

5. Съображения за CDI

Можем да посочим следните предимства на CDI прехващачите:

  • Това е стандартна характеристика на спецификацията на Джакарта EE
  • Някои библиотеки за внедряване на CDI могат да се използват в Java SE
  • Може да се използва, когато нашият проект има сериозни ограничения за библиотеките на трети страни

Недостатъците на CDI прехващачите са следните:

  • Плътно свързване между клас с бизнес логика и прехващач
  • Трудно е да се види кои класове са прихванати в проекта
  • Липса на гъвкав механизъм за прилагане на прехващачи към група методи

6. Пролетен аспектJ

Spring поддържа подобна реализация на функционалността на прехващача, използвайки също синтаксиса на AspectJ.

Първо трябва да добавим следните зависимости Spring и AspectJ към POM:

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

Най-новите версии на Spring context, aspectjweaver могат да бъдат намерени в хранилището на Maven.

Вече можем да създадем прост аспект, като използваме синтаксиса на анотация на AspectJ:

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

Създадохме аспект, който се прилага за всички методи на клас SpringSuperService - който за простота изглежда така:

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

7. Spring AspectJ Aspect Apply

За да проверим този аспект наистина се отнася за услугата, нека напишем следния единичен тест:

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

В този тест инжектираме нашата услуга, извикваме метода и проверяваме резултата.

Ето как изглежда конфигурацията:

@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
  • Кривата на обучение за прехващачите AspectJ е по-висока, отколкото за прехващачите CDI

9. CDI Interceptor срещу Spring AspectJ

Ако текущият ви проект използва Spring, обмислянето на Spring AspectJ е добър избор.

Ако използвате пълноцветен сървър за приложения или вашият проект не използва Spring (или други рамки, напр. Google Guice) и е строго Джакарта EE, не остава нищо друго освен да изберете CDI прехващач.

10. Заключение

В тази статия разгледахме две реализации на модел на прехващачи: CDI прехващач и Spring AspectJ. Покрихме предимствата и недостатъците на всеки от тях.

Изходният код за примери за тази статия може да бъде намерен в нашето хранилище на GitHub.