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


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

  1. By 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. By 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 из Context#get() очень явно выражен в этом:

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

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

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

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

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

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

3 ответа:

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

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

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

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

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

@SessionScoped User user;

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

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

У меня есть Синглет, на который я использовал метод 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.получить() способ решить ее.

Thnx для этого