Ruft die JSF-verwaltete Bean nach Namen in einer beliebigen Servlet-Klasse ab

101

Ich versuche, ein benutzerdefiniertes Servlet (für AJAX / JSON) zu schreiben, in dem ich auf meinen @ManagedBeansNamen verweisen möchte . Ich hoffe zu kartieren:

http://host/app/myBean/myProperty

zu:

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

Ist es möglich, eine Bean namentlich von einem regulären Servlet zu laden? Gibt es ein JSF-Servlet oder einen Helfer, den ich dafür verwenden könnte?

Ich scheine vom Frühling verwöhnt zu sein, in dem all dies zu offensichtlich ist.

Konrad Garus
quelle
Ich bin nicht sicher, ob Sie diese neuen Anmerkungen außerhalb von JSF / EL verwenden können, aber ich würde zunächst die JSR 299-Spezifikation betrachten: jcp.org/en/jsr/detail?id=299
McDowell
Andere Personen, die Probleme mit ähnlichen Problemen haben, können auch bpcatalog.dev.java.net/ajax/jsf-ajax überprüfen (im Zusammenhang mit AJAX und Anforderungszuordnung / -behandlung , keine Bohnen mit Namen erhalten)
Konrad Garus

Antworten:

263

In einem Servlet basierten Artefakt, wie @WebServlet, @WebFilterund @WebListenerkönnen Sie ein „plain vanilla“ JSF greifen @ManagedBean @RequestScopeddurch:

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

und @ManagedBean @SessionScopedvon:

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

und @ManagedBean @ApplicationScopedvon:

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

Beachten Sie, dass dies voraussetzt, dass die Bean bereits zuvor von JSF automatisch erstellt wurde. Sonst werden diese zurückkehren null. Sie müssten dann die Bean manuell erstellen und verwenden setAttribute("beanName", bean).


Wenn Sie CDI @Namedanstelle von JSF 2.3 verwenden können @ManagedBean, ist dies noch einfacher, insbesondere weil Sie die Beans nicht mehr manuell erstellen müssen:

@Inject
private Bean bean;

Beachten Sie, dass dies bei Verwendung nicht funktioniert, @Named @ViewScopedda die Bean nur anhand des JSF-Ansichtsstatus identifiziert werden kann und nur verfügbar ist, wenn die FacesServletaufgerufen wurde. In einem Filter, der zuvor ausgeführt wird, wird der Zugriff auf ein @InjectEd @ViewScopedimmer ausgelöst ContextNotActiveException.


Nur wenn Sie drinnen sind @ManagedBean, können Sie verwenden @ManagedProperty:

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

Beachten Sie, dass dies nicht in einem nicht funktioniert @Namedoder @WebServletoder einem anderen Artefakt. Es funktioniert wirklich @ManagedBeannur im Inneren .


Wenn Sie sich nicht in einem befinden @ManagedBean, das jedoch FacesContextverfügbar ist (dh FacesContext#getCurrentInstance()nicht zurückkehrt null), können Sie auch Folgendes verwenden Application#evaluateExpressionGet():

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

was wie folgt bequem sein kann:

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

und kann wie folgt verwendet werden:

Bean bean = findBean("bean");

Siehe auch:

BalusC
quelle
9
Ihr zweiter Vorschlag, nur die Bohne zu injizieren, war so erstaunlich einfach, dass ich ihn völlig übersehen hatte. Wie immer ist Ihre Antwort perfekt auf den Punkt. Vielen Dank für Ihre Arbeit hier auf SO.
30.
2
In der Zwischenzeit (ab JSF 2.2) wurde die Methode evaluExpressionGet anscheinend um einen dritten Parameter erweitert, mit dem die erwartete Klasse angegeben werden kann, sodass kein Casting mehr erforderlich ist. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli
1
@Marc: War von Anfang an dabei. War nur ein Überbleibsel eines Copypaste-Fehlers, denke ich. Antwort wurde korrigiert. Vielen Dank für die Benachrichtigung.
BalusC
FacesContextist verfügbar, obwohl die staticUtility-Methode findBean()in einer einfachen Java-Klasse definiert ist. Wie ist es dort in einer einfachen Java-Klasse verfügbar, die nicht von JSF verwaltet wird?
Winzig
1
@Tiny: Es wird wiederum von einem JSF-Artefakt innerhalb desselben Threads aufgerufen.
BalusC
11

Ich benutze die folgende Methode:

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);
}

Auf diese Weise kann ich das zurückgegebene Objekt typisiert abrufen.

John Yeary
quelle
3
Dies wird bereits durch die aktuell akzeptierte Antwort und sogar auf bequemere Weise abgedeckt ( ClassArgument ist in diesem Konstrukt nämlich unnötig).
BalusC
3

Haben Sie einen Ansatz wie auf diesem Link ausprobiert? Ich bin nicht sicher, ob createValueBinding()noch verfügbar ist, aber Code wie dieser sollte von einem einfachen alten Servlet aus zugänglich sein. Dies erfordert, dass Bean bereits vorhanden ist.

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.
quelle
Dies funktioniert wahrscheinlich nicht in einem normalen Servlet. Der FacesContext ist ein Thread-lokales Artefakt pro Anforderung, das vom JSF-Lebenszyklus (normalerweise das FacesServlet) eingerichtet wurde.
McDowell
7
ValueBinding ist seit JSF 1.2 vor über 4 Jahren veraltet.
BalusC
@ BalusC: Es zeigt, wie aktuell ich bin lol. Nebenbei bemerkt erweist sich die Verwendung einer Suchmaschine zur Erforschung von Techniken als kontraproduktiv mit all den alten Informationen da draußen. @ McDowell: Das macht eigentlich Sinn. Ich werde einen Test machen, um zu sehen, was passiert.
James P.
3

Sie können die verwaltete Bean erhalten, indem Sie den Namen übergeben:

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
quelle
Ich versuche dies von einem Servlet aus zu tun, aber es funktioniert nicht.
Fernando Pie
0

Ich hatte die gleiche Anforderung.

Ich habe den folgenden Weg benutzt, um es zu bekommen.

Ich hatte Sitzungsbohnen.

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

Ich habe den folgenden Code in meiner Servlet-Methode doPost () verwendet.

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

es hat mein Problem gelöst.

Anil
quelle
Welche Art von Servlet verwenden Sie? Kumpel
Fernando Pie
1
Es ist HttpServlet.
Anil
-1

Ich benutze das:

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;
}

Und dann anrufen:

MyManageBean bean = getBean(MyManageBean.class);

Auf diese Weise können Sie Ihren Code umgestalten und Verwendungen problemlos verfolgen.

CHRONONON
quelle