Перезагрузка контекста CDI во время выполнения

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

class BeanA {

 String url;

 @PostConstruct
 void init(){
  url = UrlFactory.getNewUrl()
 }
}

class BeanB {

 @Inject
 BeanA beanA;

 @PostConstruct
 void init(){
  url = beanA.getUrl();
  doSomethingWith(url);
 }

Следовательно, когда наступает событие, BeanA и BeanB должны быть повторно инициализированы в строгом порядке, поэтому во время повторной инициализации BeanB BeanB уже знает о новом URL-адресе, инициализированном в BeanA. Можно ли это сделать во время выполнения, используя уже существующие инструменты в CDI (что-то вроде Spring: AutowireCapableBeanFactory)? Я обнаружил, что EJB уже имеет аннотацию @DependsOn, которая способна создавать порядок компонентов во время запуска приложения.

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

Существуют ли какие-либо другие существующие методы, которые действительно решают мою проблему?


person Igor Dumchykov    schedule 26.02.2020    source источник
comment
Попробуйте использовать Provider<T> или Instance<T>: подробнее здесь   -  person Renato    schedule 26.02.2020
comment
Стоит отметить, что прокси-серверы CDI Inject не внедряют фактические экземпляры. Прокси-серверы CDI направляют вас к правильному экземпляру компонента при вызове метода прокси-сервера.   -  person Jonathan S. Fisher    schedule 26.02.2020


Ответы (1)


Во-первых, создайте метод производителя, который будет @Produces вашим String типизированным url значением (возможно, создайте аннотацию квалификатора @URL для дальнейшей идентификации). Произведите это в области @Dependent. Что-то вроде:

@Produces @Dependent @URL private static final String produceUrl() {
  return UrlFactory.getNewUrl();
}

Теперь, где бы кто ни делал @Inject @URL private String url, он получит новое String представление URL-адреса из вашего UrlFactory.

Что еще более важно, каждый раз, когда кто-либо выполняет @Inject @URL private Provider<String> urlProvider;, он получает Provider, чей метод get() при вызове возвращает самое последнее и самое большое значение URL.

Это может быть все, что вам нужно, если вы посмотрите на это правильно.

Если это не все, что вам нужно, заставьте BeanA сделать это:

@Inject
@URL
private Provider<String> urlProvider;

…а затем в его (опущенном выше) методе getUrl() сделайте следующее:

public String getUrl() {
  return this.urlProvider.get();
}

Вы будете получать новый String каждый раз.

Если вы сделали это, вы можете просто добавить BeanA к вашему методу наблюдателя и передать его вам:

private static final void onEvent(@Observes final YourEvent event, final BeanA beanA) {
  final String url = beanA.getUrl(); // latest and greatest      
}
person Laird Nelson    schedule 27.02.2020
comment
Спасибо, Лэрд, за ответ. Но что, если UrlFactory также прослушивает YouEvent и получает новый URL-адрес от этого события? В таком случае, когда наступит событие, UrlFactory должен получить новый url и только после этого может быть использован в зависимых bean-компонентах. Можно ли как-то сказать: BeanA зависит от UrlFactory, BeanB зависит от UrlFactory, чтобы позволить UrlFactory повторно инициализировать свое состояние перед повторной инициализацией BeanA и BeanB? - person Igor Dumchykov; 27.02.2020