Uzyskaj zarządzany komponent bean JSF według nazwy w dowolnej klasie związanej z serwletem

101

Próbuję napisać niestandardowy serwlet (dla AJAX / JSON), w którym chciałbym odwoływać się do mojej @ManagedBeansnazwy. Mam nadzieję, że zmapuję:

http://host/app/myBean/myProperty

do:

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

Czy można załadować komponent bean według nazwy ze zwykłego serwletu? Czy istnieje serwlet JSF lub pomocnik, którego mógłbym użyć?

Wydaje się, że rozpieszcza mnie wiosna, w której to wszystko jest zbyt oczywiste.

Konrad Garus
źródło
Nie jestem pewien, czy możesz użyć tych nowych adnotacji poza JSF / EL, ale zacznę od spojrzenia na specyfikację JSR 299: jcp.org/en/jsr/detail?id=299
McDowell,
Inne osoby, które mają problemy z podobnymi problemami, mogą również sprawdzić bpcatalog.dev.java.net/ajax/jsf-ajax (związane z AJAX i żądać mapowania / obsługi, nie dostają fasoli po imieniu)
Konrad Garus

Odpowiedzi:

263

W artefaktu oparte serwletu, takich jak @WebServlet, @WebFilteri @WebListenermożna złapać „plain vanilla” JSF @ManagedBean @RequestScopedprzez:

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

i @ManagedBean @SessionScopedprzez:

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

i @ManagedBean @ApplicationScopedprzez:

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

Zwróć uwagę, że wymaga to wcześniejszego automatycznego utworzenia ziarna przez JSF. W przeciwnym razie te powrócą null. Następnie musisz ręcznie utworzyć fasolę i użyć setAttribute("beanName", bean).


Jeśli możesz użyć CDI @Namedzamiast JSF 2.3, który jest przestarzały @ManagedBean, to jest to jeszcze prostsze, zwłaszcza że nie musisz już ręcznie tworzyć komponentów bean:

@Inject
private Bean bean;

Zwróć uwagę, że to nie zadziała, gdy używasz, @Named @ViewScopedponieważ komponent bean można zidentyfikować tylko na podstawie stanu widoku JSF i jest to dostępne tylko wtedy, gdy FacesServletzostał wywołany. Więc w filtrze, który działa wcześniej, dostęp do @Injected @ViewScopedzawsze wyrzuci ContextNotActiveException.


Dopiero gdy jesteś w środku @ManagedBean, możesz użyć @ManagedProperty:

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

Należy pamiętać, że to nie działa wewnątrz @Namedlub @WebServletlub innego artefaktu. To naprawdę działa @ManagedBeantylko w środku .


Jeśli nie jesteś w środku a @ManagedBean, ale FacesContextjest łatwo dostępny (tj. FacesContext#getCurrentInstance()Nie zwraca null), możesz również użyć Application#evaluateExpressionGet():

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

które można uprościć w następujący sposób:

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

i może być używany w następujący sposób:

Bean bean = findBean("bean");

Zobacz też:

BalusC
źródło
9
Twoja druga sugestia dotycząca samego wstrzyknięcia fasoli była tak niesamowicie prosta, że ​​całkowicie ją przeoczyłem. Jak zawsze, twoja odpowiedź jest perfekcyjnie na temat. Dziękuję bardzo za twoją pracę nad SO.
jnt30
2
W międzyczasie (mówiąc o JSF 2.2) wygląda na to, że metoda assessmentExpressionGet została rozszerzona o trzeci parametr, który pozwala określić oczekiwaną klasę, dzięki czemu rzutowanie nie będzie już potrzebne. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli
1
@Marc: Jest od początku. To chyba pozostałość po błędzie copypaste. Odpowiedź została poprawiona. Dziękuję za powiadomienie.
BalusC
FacesContextjest dostępna, mimo że staticmetoda narzędzia findBean()jest zdefiniowana w zwykłej klasie Java. Jak jest tam dostępna w zwykłej klasie Java, która nie jest zarządzana przez JSF?
Mały
1
@Tiny: jest z kolei wywoływany przez artefakt JSF w tym samym wątku.
BalusC,
11

Używam następującej metody:

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

To pozwala mi uzyskać zwrócony obiekt w sposób wpisany na maszynie.

John Yeary
źródło
3
Jest to już objęte obecnie akceptowaną odpowiedzią, a nawet w wygodniejszy sposób ( Classargument jest zbędny w tej konstrukcji).
BalusC,
3

Czy próbowałeś podejścia podobnego do tego linku? Nie jestem pewien, czy createValueBinding()jest nadal dostępny, ale taki kod powinien być dostępny ze zwykłego starego serwletu. To wymaga, aby fasola już istniała.

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.
źródło
To prawdopodobnie nie zadziała w zwykłym serwlecie. FacesContext jest artefaktem lokalnym wątku na żądanie, ustawianym przez cykl życia JSF (zwykle serwlet FacesServlet).
McDowell
7
ValueBinding jest przestarzałe od JSF 1.2 ponad 4 lata temu.
BalusC
@BalusC: To pokazuje, jak aktualny jestem lol. Na marginesie, korzystanie z wyszukiwarki do badania technik okazuje się przynosić skutki odwrotne do zamierzonych w przypadku wszystkich starych informacji. @McDowell: To właściwie ma sens. Zrobię test, żeby zobaczyć, co się stanie.
James P.,
3

Możesz uzyskać zarządzaną fasolę, przekazując nazwę:

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
źródło
Próbuję to zrobić z serwletu, ale to nie działa.
Fernando Pie
0

Miałem ten sam wymóg.

Użyłem poniższego sposobu, aby to uzyskać.

Miałem fasolę sesyjną.

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

Użyłem poniższego kodu w mojej metodzie doPost () serwletu.

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

rozwiązało mój problem.

Indygowiec
źródło
Jakiego rodzaju serwletu używasz? mate
Fernando Pie
1
To jest HttpServlet.
Anil
-1

Używam tego:

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

A potem zadzwoń:

MyManageBean bean = getBean(MyManageBean.class);

W ten sposób możesz bez problemu refaktoryzować swój kod i śledzić jego użycie.

ChRoNoN
źródło