Obtenez le bean géré JSF par nom dans n'importe quelle classe liée à un servlet

101

J'essaie d'écrire un servlet personnalisé (pour AJAX / JSON) dans lequel je voudrais référencer mon @ManagedBeansnom. J'espère cartographier:

http://host/app/myBean/myProperty

à:

@ManagedBean(name="myBean")
public class MyBean {
    public String getMyProperty();
}

Est-il possible de charger un bean par son nom à partir d'un servlet normal? Y a-t-il un servlet ou un assistant JSF que je pourrais utiliser?

J'ai l'impression d'être gâté par le printemps où tout cela est trop évident.

Konrad Garus
la source
Je ne sais pas si vous pouvez utiliser ces nouvelles annotations en dehors de JSF / EL, mais je commencerais par regarder la spécification JSR 299: jcp.org/en/jsr/detail?id=299
McDowell
D'autres personnes ayant des problèmes avec des problèmes similaires peuvent également consulter bpcatalog.dev.java.net/ajax/jsf-ajax (lié à AJAX et demander le mappage / la gestion, ne pas obtenir les beans par nom)
Konrad Garus

Réponses:

263

Dans un artefact basé sur un servlet, tel que @WebServlet, @WebFilteret @WebListener, vous pouvez récupérer un JSF "plain vanilla" @ManagedBean @RequestScopeden:

Bean bean = (Bean) request.getAttribute("beanName");

et @ManagedBean @SessionScopedpar:

Bean bean = (Bean) request.getSession().getAttribute("beanName");

et @ManagedBean @ApplicationScopedpar:

Bean bean = (Bean) getServletContext().getAttribute("beanName");

Notez que cela nécessite que le bean soit déjà créé automatiquement par JSF au préalable. Sinon, ceux-ci reviendront null. Vous devrez ensuite créer manuellement le bean et l'utiliser setAttribute("beanName", bean).


Si vous êtes en mesure d'utiliser CDI @Namedau lieu de la version obsolète de JSF 2.3 @ManagedBean, c'est encore plus facile, en particulier parce que vous n'avez plus besoin de créer manuellement les beans:

@Inject
private Bean bean;

Notez que cela ne fonctionnera pas lorsque vous l'utilisez @Named @ViewScopedcar le bean ne peut être identifié que par l'état d'affichage JSF et n'est disponible que lorsque le FacesServleta été appelé. Donc, dans un filtre qui s'exécute avant cela, accéder à un @Injected @ViewScopedsera toujours lancé ContextNotActiveException.


Ce n'est que lorsque vous êtes à l'intérieur que @ManagedBeanvous pouvez utiliser @ManagedProperty:

@ManagedProperty("#{bean}")
private Bean bean;

Notez que cela ne fonctionne pas dans un @Namedou @WebServletou tout autre artefact. Cela ne fonctionne vraiment @ManagedBeanqu'à l' intérieur .


Si vous n'êtes pas à l'intérieur de a @ManagedBean, mais que le FacesContextest facilement disponible (c'est FacesContext#getCurrentInstance()-à- dire qu'il ne revient pas null), vous pouvez également utiliser Application#evaluateExpressionGet():

FacesContext context = FacesContext.getCurrentInstance();
Bean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", Bean.class);

qui peut être commode comme suit:

@SuppressWarnings("unchecked")
public static <T> T findBean(String beanName) {
    FacesContext context = FacesContext.getCurrentInstance();
    return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
}

et peut être utilisé comme suit:

Bean bean = findBean("bean");

Voir également:

BalusC
la source
9
Votre deuxième suggestion à propos de l'injection du haricot était tellement simple que je l'avais totalement négligée. Comme toujours, votre réponse est parfaitement pertinente. Merci beaucoup pour votre travail ici sur SO.
jnt30
2
En attendant (à partir de JSF 2.2), il semble que la méthode evaluExpressionGet a été étendue avec un troisième paramètre qui permet de spécifier la classe attendue afin que la conversion ne soit plus nécessaire. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli
1
@Marc: A été depuis le début. C'était juste un reste d'une erreur de copypaste je suppose. La réponse a été corrigée. Merci de l'avoir notifié.
BalusC
FacesContextest disponible même si la staticméthode utilitaire findBean()est définie dans une classe Java simple. Comment y est-il disponible dans une classe Java simple qui n'est pas gérée par JSF?
Minuscule
1
@Tiny: il est à son tour appelé par un artefact JSF dans le même thread.
BalusC
11

J'utilise la méthode suivante:

public static <T> T getBean(final String beanName, final Class<T> clazz) {
    ELContext elContext = FacesContext.getCurrentInstance().getELContext();
    return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elContext, null, beanName);
}

Cela me permet d'obtenir l'objet retourné de manière typée.

John Yeary
la source
3
Ceci est déjà couvert par la réponse actuellement acceptée et même d'une manière plus pratique (l' Classargument est notamment inutile dans cette construction).
BalusC
3

Avez-vous essayé une approche comme sur ce lien? Je ne sais pas s'il createValueBinding()est toujours disponible, mais un code comme celui-ci devrait être accessible à partir d'un ancien servlet. Cela nécessite que le bean existe déjà.

http://www.coderanch.com/t/211706/JSF/java/access-managed-bean-JSF-from

 FacesContext context = FacesContext.getCurrentInstance();  
 Application app = context.getApplication();
 // May be deprecated
 ValueBinding binding = app.createValueBinding("#{" + expr + "}"); 
 Object value = binding.getValue(context);
James P.
la source
Cela ne fonctionnera probablement pas dans un servlet normal. Le FacesContext est un artefact local de thread par requête configuré par le cycle de vie JSF (généralement le FacesServlet).
McDowell
7
ValueBinding est obsolète depuis JSF 1.2 il y a plus de 4 ans.
BalusC
@BalusC: Cela montre à quel point je suis à jour. En passant, l'utilisation d'un moteur de recherche pour rechercher des techniques s'avère contre-productive avec toutes les anciennes informations disponibles. @McDowell: Cela a du sens. Je vais faire un test juste pour voir ce qui se passe.
James P.
3

Vous pouvez obtenir le bean géré en passant le nom:

public static Object getBean(String beanName){
    Object bean = null;
    FacesContext fc = FacesContext.getCurrentInstance();
    if(fc!=null){
         ELContext elContext = fc.getELContext();
         bean = elContext.getELResolver().getValue(elContext, null, beanName);
    }

    return bean;
}
Soujanya
la source
J'essaye de le faire à partir d'un servlet mais cela ne fonctionne pas.
Fernando Pie
0

J'avais la même exigence.

J'ai utilisé la méthode ci-dessous pour l'obtenir.

J'ai eu un haricot à portée de session.

@ManagedBean(name="mb")
@SessionScopedpublic 
class ManagedBean {
     --------
}

J'ai utilisé le code ci-dessous dans ma méthode servlet doPost ().

ManagedBean mb = (ManagedBean) request.getSession().getAttribute("mb");

cela a résolu mon problème.

Anil
la source
Quel type de servlet utilisez-vous? mate
Fernando Pie
1
C'est HttpServlet.
Anil
-1

J'utilise ceci:

public static <T> T getBean(Class<T> clazz) {
    try {
        String beanName = getBeanName(clazz);
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().evaluateExpressionGet(facesContext, "#{" + beanName + "}", clazz);
    //return facesContext.getApplication().getELResolver().getValue(facesContext.getELContext(), null, nomeBean);
    } catch (Exception ex) {
        return null;
    }
}

public static <T> String getBeanName(Class<T> clazz) {
    ManagedBean managedBean = clazz.getAnnotation(ManagedBean.class);
    String beanName = managedBean.name();

    if (StringHelper.isNullOrEmpty(beanName)) {
        beanName = clazz.getSimpleName();
        beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
    }

    return beanName;
}

Et puis appelez:

MyManageBean bean = getBean(MyManageBean.class);

De cette façon, vous pouvez refactoriser votre code et suivre les utilisations sans problème.

ChRoNoN
la source