web-dev-qa-db-fra.com

Méthode canonique pour obtenir une instance de bean géré CDI: BeanManager # getReference () vs Context # get ()

Je pensais qu'il y avait deux façons générales d'obtenir une instance de bean géré CDI créée automatiquement via BeanManager lorsque vous avez uniquement un Bean<T> Pour commencer (qui est créé en fonction de sur Class<T>):

  1. Par BeanManager#getReference() , qui est le plus souvent affiché dans les extraits:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  2. Par Context#get() , qui est moins souvent affiché dans les extraits:

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

Dans les effets, ils font finalement exactement la même chose: renvoyer une référence proxy à l'instance de bean gérée CDI actuelle et crée automatiquement l'instance de bean si elle n'existe pas déjà dans la portée.

Mais ils le font un peu différemment: la BeanManager#getReference() crée toujours une toute nouvelle instance de proxy, tandis que la Context#get() réutilise une instance de proxy existante si elle a déjà été créée auparavant. Cela est évident lorsque le code ci-dessus est exécuté dans une méthode d'action d'une instance TestBean existante:

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

javadoc de Context#get() est très explicite en ceci:

Renvoyez une instance existante d'un certain type contextuel ou créez une nouvelle instance en appelant Contextual.create (CreationalContext) et renvoyez la nouvelle instance.

tandis que javadoc de BeanManager#getReference() n'est pas assez explicite à ce sujet:

Obtient une référence contextuelle pour un certain bean et un certain type de bean du bean.

Cela m'a dérouté. Quand utilisez-vous l'un ou l'autre? Pour les deux façons dont vous avez besoin d'une instance Bean<T> De toute façon, à partir de laquelle la classe de bean et la portée du bean sont facilement disponibles, ce qui est requis en tant que complément argument. Je ne peux pas imaginer pourquoi ils devraient être fournis en externe dans ce cas spécifique.

Je peux imaginer que Context#get() est plus efficace en mémoire car il ne crée pas inutilement une autre instance de proxy référençant la même instance de bean sous-jacente, mais trouve et réutilise simplement une instance de proxy existante.

Cela m'amène à la question suivante: quand exactement la BeanManager#getReference() est-elle plus utile que Context#get()? Il est plus souvent affiché dans des extraits de code et plus souvent recommandé comme solution, mais il ne crée inutilement qu'un nouveau proxy même s'il en existe déjà un.

38
BalusC

beanManager # getReference vous donne une nouvelle instance d'un proxy client mais le proxy client transmettra les appels de méthode à l'instance contextuelle actuelle d'un contexte particulier. Une fois que vous avez obtenu le proxy et que vous le conservez, les appels de méthode seront invoqués sur l'instance actuelle (par exemple, la requête actuelle). Il est également utile si l'instance contextuelle n'est pas sérialisable - le proxy client sera et se reconnectera après l'avoir désérialisé.

BeanManager # getContext obtient l'instance cible sans proxy client. Vous pouvez toujours voir un proxy Weld dans le nom de la classe, mais il s'agit d'une sous-classe améliorée qui fournit l'interception et la décoration. Si le bean n'est pas intercepté ni décoré, ce sera un exemple simple du bean donné.

Habituellement, (1) est plus approprié sauf si vous avez un cas d'utilisation spécial où vous devez accéder directement à l'instance cible (par exemple pour accéder à ses champs).

Ou en d'autres termes

1) BeanManager # getReference renverra une 'référence contextuelle', avec un proxy de portée normale pour le bean. Si un bean a avec @SessionScoped Comme

@SessionScoped User user;

Ensuite, l'utilisateur de référence contextuelle pointera vers l'instance d'utilisateur respective (l '"instance contextuelle") de la session en cours pour chaque appel. Deux appels différents à user.getName() à partir de deux navigateurs Web différents vous donneront des réponses différentes.

2) Context # get () renverra une "instance contextuelle" interne sans le proxy de portée normal. Ce n'est généralement rien qu'un utilisateur doit appeler lui-même. Si vous obtenez le User user Pour "Bob" de cette façon et le stockez dans un bean @ApplicationScoped Ou dans une variable statique, il restera toujours l'utilisateur "Bob" - même pour le Web demandes d'autres navigateurs! Vous obtiendrez une instance directe non mandatée.

36
Asif Bhutto

J'ai un Singleton que j'utilisais la méthode getReference () pour obtenir la référence. Même si le singleton a déjà été initialisé, le proxy créé via getReference () a appelé @PostConstruct chaque fois que getReference () a été utilisé.

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

En passant à la méthode getContext (). Get (), les appels proxy @PostConstruct inutiles ne sont plus effectués.

1
tinman13

Cela a été très utile lors de l'intégration de CDI avec javafx, le fait était que j'avais besoin d'une référence à la bonne cause d'objet de portée et non au proxy de la portée dépendante ...

J'ai utilisé une méthode de producteur pour obtenir un javaFX Node qui est injecté dans le contrôleur comme ceci:

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

mais lorsque vous utilisez le BeanManager # getReference (), le proxy que je récupère "mange" toutes les valeurs définies par FXMLLoader, la méthode getContext.get () l'a résolu.

Thnx pour cela

0
brammie