Канонический способ получения экземпляра управляемого компонента CDI: BeanManager#getReference() vs Context#get()

Я понял, что есть два основных способа получить автоматически созданный экземпляр управляемого компонента CDI через BeanManager когда только Bean<T> для начала (который создается на основе Class<T>):

  1. От BeanManager#getReference(), что чаще всего показано во фрагментах:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  2. От Context#get(), что реже показывается во фрагментах:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean2 = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
    

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

Но они делают это немного по-другому: BeanManager#getReference() всегда создает совершенно новый экземпляр прокси, а Context#get() повторно использует существующий экземпляр прокси, если он уже был создан ранее. Это очевидно, когда приведенный выше код выполняется в методе действия существующего TestBean пример:

System.out.println(testBean1 == testBean2); // false
System.out.println(testBean1 == this); // false
System.out.println(testBean2 == this); // true

Javadoc of Context#get() очень явно в этом:

Верните существующий экземпляр определенного контекстуального типа или создайте новый экземпляр, вызвав Contextual.create(CreationalContext), и верните новый экземпляр.

в то время как Javadoc BeanManager#getReference() не достаточно ясно об этом:

Получает контекстную ссылку для определенного компонента и определенного типа компонента.

Это меня запутало. Когда вы используете один или другой? Для обоих способов вам нужен Bean<T> экземпляр в любом случае, из которого легко доступны класс bean-компонента и область действия bean-компонента, что требуется в качестве дополнительного аргумента. Я не могу себе представить, почему они должны быть поставлены извне в этом конкретном случае.

Я могу представить, что Context#get() более эффективно использует память, поскольку не создает ненужного другого экземпляра прокси, ссылающегося на тот же базовый экземпляр компонента, а просто находит и повторно использует существующий экземпляр прокси.

Это ставит меня к следующему вопросу: когда именно BeanManager#getReference() более полезный, чем Context#get()? Он чаще показывается во фрагментах и ​​чаще всего рекомендуется в качестве решения, но он только излишне создает новый прокси, даже если он уже существует.

3 ответа

Решение

beanManager#getReference дает вам новый экземпляр клиентского прокси, но клиентский прокси будет перенаправлять вызовы методов в текущий контекстный экземпляр определенного контекста. Как только вы получите прокси и сохраните его, вызовы методов будут вызваны на текущем экземпляре (например, текущем запросе). Также полезно, если контекстный экземпляр не сериализуем - клиентский прокси будет и будет повторно подключен после того, как вы его десериализовали.

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

Обычно (1) более подходит, если у вас нет особого варианта использования, где вам нужно напрямую обращаться к целевому экземпляру (например, для доступа к его полям).

Или другими словами

1) BeanManager#getReference вернет "контекстную ссылку " с обычным прокси-сервером для компонента. Если боб имеет с @SessionScoped как

@SessionScoped User user;

Затем пользователь контекстной ссылки будет "указывать" на соответствующий экземпляр пользователя ("Контекстный экземпляр") текущего сеанса для каждого вызова. Два разных вызова user.getName() из двух разных веб-браузеров даст вам разные ответы.

2) Context # get () вернет внутреннее "Contextual Instance" без обычного прокси-сервера. Обычно это пользователь не должен вызывать сам. Если вы получите User user для "Боба" таким образом и хранить его в @ApplicationScoped боб или в статической переменной, то он всегда останется пользователем "Боб" - даже для веб-запросов из других браузеров! Вы получите прямой экземпляр без посредников.

У меня есть Singleton, который я использовал метод getReference(), чтобы получить ссылку. Несмотря на то, что синглтон уже был инициализирован, прокси-сервер, созданный через getReference(), вызывается @PostConstruct каждый раз, когда используется getReference().

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

При переключении на метод getContext (). Get () ненужные прокси-вызовы @PostConstruct больше не выполняются.

Это было очень полезно при интеграции CDI с javafx, дело в том, что мне нужна была ссылка на правильную причину объекта, а не прокси зависимой области видимости...

Я использовал метод продюсера, чтобы получить узел javaFX, который вставляется в контроллер следующим образом:

@Inject
@ApplicationScoped
@FXMLFile("javafx/wares.fxml")
@FXMLController(WaresController.class)
Parent wares;

но при использовании BeanManager#getReference() прокси-сервер, который я получаю, "съедает" все значения, которые установлены FXMLLoader, способ getContext.get() решил эту проблему.

Спасибо за это

Другие вопросы по тегам