Канонический способ получить управляемый 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()
? Он чаще всего проявляется в фрагментах и чаще всего рекомендуется в качестве решения, но он только излишне создает новый прокси-сервер, даже если он уже существует.
Ответы
Ответ 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 для этого