Рубрики
Без рубрики

CDI Перехватчик против Spring AspectJ

Быстрый взгляд на основные различия между двумя различными библиотеками перехватчиков: CDI Interceptor и Spring AspectJ.

Автор оригинала: baeldung.

1. введение

Шаблон перехватчика обычно используется для добавления новых сквозных функций или логики в приложение и имеет надежную поддержку в большом количестве библиотек.

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

2. Настройка проекта перехватчика CDI

CDI официально поддерживается для Jakarta EE, но некоторые реализации обеспечивают поддержку использования CDI в среде Java SE. Weld можно рассматривать как один из примеров реализации CDI, которая поддерживается в Java SE.

Чтобы использовать CDI, нам нужно импортировать библиотеку сварных швов в наш POM:


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

Самую последнюю библиотеку сварных швов можно найти в репозитории Maven .

Давайте теперь создадим простой перехватчик.

3. Введение перехватчика CDI

Чтобы обозначить классы, которые нам нужно было перехватить, давайте создадим привязку перехватчика:

@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.перехватчик.Аргумент InvocationContext возвращает java.lang.Объект , и может вызвать Исключение .

И поэтому, когда мы аннотируем метод с помощью нового интерфейса @Audit , сначала будет вызван метод audit, и только затем будет запущен целевой метод.

4. Примените перехватчик CDI

Давайте применим созданный перехватчик к некоторой бизнес-логике:

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

В этом быстром тесте мы сначала получаем компонент Super Service из контейнера, затем вызываем на нем бизнес-метод deliver Service и проверяем, что перехватчик AuditedInterceptor был фактически вызван путем проверки его переменных состояния.

Также у нас есть @Before и @After аннотированные методы, в которых мы инициализируем и выключаем Weldcontainer соответственно.

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

Мы можем отметить следующие преимущества перехватчиков CDI:

  • Это стандартная функция спецификации Jakarta EE
  • Некоторые библиотеки реализаций CDI могут использоваться в Java SE
  • Может использоваться, когда наш проект имеет серьезные ограничения на сторонние библиотеки

Недостатками перехватчиков CDI являются следующие:

  • Тесная связь между классом с бизнес-логикой и перехватчиком
  • Трудно понять, какие классы перехватываются в проекте
  • Отсутствие гибкого механизма применения перехватчиков к группе методов

6. Весенний аспект

Spring также поддерживает аналогичную реализацию функций перехватчика с использованием синтаксиса AspectJ.

Сначала нам нужно добавить следующие зависимости Spring и AspectJ в POM:


    org.springframework
    spring-context
    5.2.8.RELEASE


    org.aspectj
    aspectjweaver
    1.9.2

Самые последние версии Spring context , aspectj weaver можно найти в репозитории 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;
    }
}

Мы создали аспект, который применим ко всем методам класса Spring Super Service , который для простоты выглядит следующим образом:

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

7. Весенний аспект

Чтобы проверить, действительно ли этот аспект применим к службе, давайте напишем следующий модульный тест:

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

Один важный аспект здесь в аннотации @EnableAspectJAutoProxy , которая обеспечивает поддержку обработки компонентов, помеченных аннотацией AspectJ @Aspect , аналогичной функциональности, найденной в XML-элементе Spring.

8. Соображения по весеннему аспекту

Давайте укажем на некоторые преимущества использования Spring AspectJ:

  • Перехватчики отделены от бизнес – логики
  • Перехватчики могут извлечь выгоду из внедрения зависимостей
  • Перехватчик имеет всю информацию о конфигурации в себе
  • Добавление новых перехватчиков не потребует дополнения существующего кода
  • Перехватчик имеет гибкий механизм для выбора методов перехвата
  • Может использоваться без использования EE

И конечно же несколько недостатков:

  • Вам необходимо знать синтаксис AspectJ для разработки перехватчиков
  • Кривая обучения для перехватчиков AspectJ выше, чем для перехватчиков CDI

9. Перехватчик CDI против Spring AspectJ

Если ваш текущий проект использует Spring, то рассмотрение Spring AspectJ является хорошим выбором.

Если вы используете полномасштабный сервер приложений или ваш проект не использует Spring (или другие фреймворки, например Google Guice) и строго ориентирован на EE, то вам ничего не остается, как выбрать перехватчик CDI.

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

В этой статье мы рассмотрели две реализации шаблона перехватчика: CDI interceptor и Spring AspectJ. Мы рассмотрели преимущества и недостатки каждого из них.

Исходный код для примеров этой статьи можно найти в нашем репозитории на GitHub .