Подтвердить что ты не робот

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

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

  • BeanManager#getReference(), что чаще всего показано в сниппеты:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  • 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 bean экземпляр и автоматически создает экземпляр bean, если он еще не существует в области.

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

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

javadoc из Context#get() очень явственно в этом:

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

@SessionScoped User user;

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

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

Ответ 2

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

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

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

Ответ 3

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

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

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

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

Thnx для этого