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

Портативное расширение CDI и взлетно-посадочная полоса

Узнайте, как создать портативное расширение CDI

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

1. Обзор

В этом уроке мы рассмотрим интересную функцию CDI (внедрение контекста и зависимостей), называемую портативным расширением CDI.

Сначала мы начнем с понимания того, как это работает, а затем посмотрим, как написать расширение. Мы рассмотрим шаги по внедрению модуля интеграции CDI для Flyway, чтобы мы могли выполнить миграцию базы данных при запуске контейнера CDI.

Этот учебник предполагает базовое понимание CDI. Ознакомьтесь с этой статьей для ознакомления с CDI.

2. Что такое портативное расширение CDI?

Портативное расширение CDI-это механизм, с помощью которого мы можем реализовать дополнительные функции поверх контейнера CDI. Во время начальной загрузки контейнер CDI сканирует путь к классам и создает метаданные об обнаруженных классах.

Во время этого процесса сканирования контейнер CDI запускает множество событий инициализации, которые могут наблюдаться только расширениями . Именно здесь в игру вступает портативное расширение CDI.

Переносимое расширение CDI отслеживает эти события, а затем изменяет или добавляет информацию в метаданные, созданные контейнером.

3. Зависимости Maven

Давайте начнем с добавления необходимой зависимости для API CDI в pom.xml . Этого достаточно для реализации пустого расширения.


    javax.enterprise
    cdi-api
    2.0.SP1

И для запуска приложения мы можем использовать любую совместимую реализацию CDI. В этой статье мы будем использовать реализацию сварки.


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

Вы можете проверить, были ли выпущены какие-либо новые версии API и реализации в Maven Central.

4. Запуск взлетно-посадочной полосы в среде, отличной от CDI

Прежде чем мы начнем интегрировать Flyway и CDI, мы должны сначала рассмотреть, как запустить его в контексте, отличном от CDI.

Итак, давайте взглянем на следующий пример, взятый с официального сайта Flyway :

DataSource dataSource = //...
Flyway flyway = new Flyway();
flyway.setDataSource(dataSource);
flyway.migrate();

Как мы видим, мы используем только экземпляр Flyway , которому нужен Источник данных экземпляр.

Наше портативное расширение CDI позже создаст Flyway и Источник данных компоненты. Для целей этого примера мы будем использовать встроенную базу данных H2 и предоставим Источник данных свойства через DataSourceDefinition аннотацию.

5. События инициализации контейнера CDI

При начальной загрузке приложения контейнер CDI запускается с загрузки и создания экземпляров всех переносимых расширений CDI. Затем в каждом расширении он ищет и регистрирует методы наблюдателя событий инициализации, если таковые имеются. После этого он выполняет следующие действия:

  1. Срабатывает Перед обнаружением событие перед началом процесса сканирования
  2. Выполняет обнаружение типов, в котором он сканирует компоненты архива, и для каждого обнаруженного типа запускает событие ProcessAnnotatedType
  3. Запускает событие AfterTypeDiscovery
  4. Выполняет обнаружение компонентов
  5. Запускает событие AfterBeanDiscovery
  6. Выполняет проверку компонентов и обнаруживает ошибки определения
  7. Запускает событие AfterDeploymentValidation

Цель портативного расширения CDI состоит в том, чтобы наблюдать за этими событиями, проверять метаданные об обнаруженных компонентах, изменять эти метаданные или добавлять к ним.

В портативном расширении CDI мы можем наблюдать только эти события.

6. Написание портативного расширения CDI

Давайте посмотрим, как мы можем подключиться к некоторым из этих событий, создав наше собственное портативное расширение CDI.

6.1. Внедрение поставщика SPI

Портативное расширение CDI – это поставщик Java SPI интерфейса javax.enterprise.inject.spi.Extension. Взгляните на эту статью для ознакомления с Java SPI.

Во-первых, мы начнем с предоставления Расширения реализации. Позже мы добавим методы наблюдателя в события начальной загрузки контейнера CDI:

public class FlywayExtension implements Extension {
}

Затем мы добавляем имя файла META-INF/services/javax.enterprise.inject.spi.Extension с этим содержимым:

com.baeldung.cdi.extension.FlywayExtension

В качестве SPI это Расширение загружается перед загрузкой контейнера. Таким образом, методы наблюдателя для событий начальной загрузки CDI могут быть зарегистрированы.

6.2. Определение методов наблюдателя событий Инициализации

В этом примере мы делаем класс Flyway известным контейнеру CDI до начала процесса сканирования. Это делается в методе register Flyway Type() observer:

public void registerFlywayType(
  @Observes BeforeBeanDiscovery bbdEvent) {
    bbdEvent.addAnnotatedType(
      Flyway.class, Flyway.class.getName());
}

Здесь мы добавили метаданные о классе Flyway . С этого момента он будет вести себя так, как если бы он был отсканирован контейнером. Для этой цели мы использовали метод addAnnotatedType () .

Далее мы рассмотрим событие ProcessAnnotatedType , чтобы сделать класс Flyway управляемым компонентом CDI:

public void processAnnotatedType(@Observes ProcessAnnotatedType patEvent) {
    patEvent.configureAnnotatedType()
      .add(ApplicationScoped.Literal.INSTANCE)
      .add(new AnnotationLiteral() {})
      .filterMethods(annotatedMethod -> {
          return annotatedMethod.getParameters().size() == 1
            && annotatedMethod.getParameters().get(0).getBaseType()
              .equals(javax.sql.DataSource.class);
      }).findFirst().get().add(InjectLiteral.INSTANCE);
}

Сначала мы аннотируем класс Flyway с помощью @ApplicationScoped и @FlywayType аннотациями, затем мы ищем метод Flyway.setDataSource(источник данных) и аннотируем его с помощью @Inject.

Конечный результат вышеуказанных операций будет иметь тот же эффект, как если бы контейнер сканировал следующий Flyway боб:

@ApplicationScoped
@FlywayType
public class Flyway {
 
    //...
    @Inject
    public void setDataSource(DataSource dataSource) {
      //...
    }
}

Следующий шаг-сделать Источник данных компонент доступным для инъекции, поскольку наш Flyway компонент зависит от Источника данных компонента.

Для этого мы обработаем регистрацию источника данных компонента в контейнере и будем использовать событие AfterBeanDiscovery :

void afterBeanDiscovery(@Observes AfterBeanDiscovery abdEvent, BeanManager bm) {
    abdEvent.addBean()
      .types(javax.sql.DataSource.class, DataSource.class)
      .qualifiers(new AnnotationLiteral() {}, new AnnotationLiteral() {})
      .scope(ApplicationScoped.class)
      .name(DataSource.class.getName())
      .beanClass(DataSource.class)
      .createWith(creationalContext -> {
          DataSource instance = new DataSource();
          instance.setUrl(dataSourceDefinition.url());
          instance.setDriverClassName(dataSourceDefinition.className());
              return instance;
      });
}

Как мы видим, нам нужно Определение источника данных , которое предоставляет свойства источника данных.

Мы можем аннотировать любой управляемый компонент с помощью следующей аннотации:

@DataSourceDefinition(
  name = "ds", 
  className = "org.h2.Driver", 
  url = "jdbc:h2:mem:testdb")

Чтобы извлечь эти свойства, мы наблюдаем событие ProcessAnnotatedType вместе с аннотацией @WithAnnotations :

public void detectDataSourceDefinition(
  @Observes @WithAnnotations(DataSourceDefinition.class) ProcessAnnotatedType patEvent) {
    AnnotatedType at = patEvent.getAnnotatedType();
    dataSourceDefinition = at.getAnnotation(DataSourceDefinition.class);
}

И, наконец, мы прослушиваем событие Afterdeploymentvalidation , чтобы получить нужный компонент Flyway из контейнера CDI, а затем вызываем метод migrate() :

void runFlywayMigration(
  @Observes AfterDeploymentValidation adv, 
  BeanManager manager) {
    Flyway flyway = manager.createInstance()
      .select(Flyway.class, new AnnotationLiteral() {}).get();
    flyway.migrate();
}

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

Создание портативного расширения CDI на первый взгляд кажется сложным, но как только мы поймем жизненный цикл инициализации контейнера и SPI, посвященный расширениям, он станет очень мощным инструментом, который мы сможем использовать для создания фреймворков поверх Jakarta EE.

Как обычно, все примеры кода, показанные в этой статье, можно найти на GitHub .