Ottieni il bean gestito JSF per nome in qualsiasi classe correlata al servlet


101

Sto cercando di scrivere un servlet personalizzato (per AJAX / JSON) in cui vorrei fare riferimento al mio @ManagedBeansnome. Spero di mappare:

http://host/app/myBean/myProperty

per:

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

È possibile caricare un bean per nome da un servlet regolare? C'è un servlet o un helper JSF che potrei usare per questo?

Mi sembra di essere viziato dalla primavera in cui tutto questo è troppo ovvio.


Non sono sicuro che tu possa usare queste nuove annotazioni al di fuori di JSF / EL, ma inizierei guardando le specifiche JSR 299: jcp.org/en/jsr/detail?id=299
McDowell

Altre persone che hanno problemi con problemi simili possono anche controllare bpcatalog.dev.java.net/ajax/jsf-ajax (relativo ad AJAX e richiesta di mappatura / gestione, non ottenere i fagioli per nome)
Konrad Garus

Risposte:


263

In un artefatto basato su servlet, come @WebServlet, @WebFiltere @WebListener, puoi prendere un JSF "plain vanilla" @ManagedBean @RequestScoped:

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

e @ManagedBean @SessionScopedda:

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

e @ManagedBean @ApplicationScopedda:

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

Notare che questo prerequisito che il bean sia già autocreato da JSF in anticipo. Altrimenti questi torneranno null. Dovresti quindi creare manualmente il bean e utilizzare setAttribute("beanName", bean).


Se sei in grado di utilizzare CDI @Namedinvece del deprecato JSF 2.3 @ManagedBean, allora è ancora più facile, soprattutto perché non hai più bisogno di creare manualmente i bean:

@Inject
private Bean bean;

Nota che questo non funzionerà quando lo stai usando @Named @ViewScopedperché il bean può essere identificato solo dallo stato di visualizzazione JSF ed è disponibile solo quando FacesServletè stato invocato. Quindi in un filtro che viene eseguito prima, l'accesso a un @Injected @ViewScopedverrà sempre generato ContextNotActiveException.


Solo quando sei dentro @ManagedBean, puoi usare @ManagedProperty:

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

Nota che questo non funziona all'interno di un @Namedo @WebServleto qualsiasi altro artefatto. Funziona davvero @ManagedBeansolo all'interno .


Se non sei all'interno di un @ManagedBean, ma FacesContextè immediatamente disponibile (cioè FacesContext#getCurrentInstance()non ritorna null), puoi anche usare Application#evaluateExpressionGet():

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

che può essere convenientemente come segue:

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

e può essere utilizzato come segue:

Bean bean = findBean("bean");

Guarda anche:


9
Il tuo secondo suggerimento sull'iniezione del fagiolo era così incredibilmente semplice che l'avevo completamente trascurato. Come sempre, la tua risposta è perfettamente al punto. Grazie mille per il tuo lavoro qui su SO.
jnt30

2
Nel frattempo (parlando come di JSF 2.2) sembra che il metodo assessExpressionGet sia stato esteso con un terzo parametro che permette di specificare la classe attesa in modo che il casting non sarà più necessario. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli

1
@ Marc: è stato dall'inizio. Immagino fosse solo un residuo di un errore di copypaste. La risposta è stata corretta. Grazie per la notifica.
BalusC

FacesContextè disponibile anche se il staticmetodo di utilità findBean()è definito all'interno di una semplice classe Java. Com'è disponibile lì in una semplice classe Java che non è gestita da JSF?
Minuscolo

1
@Tiny: a sua volta viene chiamato da un artefatto JSF all'interno dello stesso thread.
BalusC

11

Uso il seguente metodo:

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

Ciò mi consente di ottenere l'oggetto restituito in modo digitato.


3
Questo è già coperto dalla risposta attualmente accettata e anche in un modo più conveniente (l' Classargomento è cioè non necessario in questo costrutto).
BalusC

3

Hai provato un approccio come su questo link? Non sono sicuro che createValueBinding()sia ancora disponibile, ma il codice come questo dovrebbe essere accessibile da un semplice vecchio Servlet. Ciò richiede che il bean esista già.

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

Questo probabilmente non funzionerà in un servlet regolare. FacesContext è un artefatto locale del thread per richiesta impostato dal ciclo di vita JSF (di solito FacesServlet).
McDowell

7
ValueBinding è deprecato da JSF 1.2 oltre 4 anni fa.
BalusC

@BalusC: mostra quanto sono aggiornato lol. In una nota a margine, l'utilizzo di un motore di ricerca per la ricerca di tecniche si sta rivelando controproducente con tutte le vecchie informazioni là fuori. @ McDowell: ha davvero senso. Farò un test solo per vedere cosa succede.
James P.

3

Puoi ottenere il bean gestito passando il nome:

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

Provo a farlo da un servlet ma non funziona.
Fernando Pie

0

Avevo lo stesso requisito.

Ho usato il modo seguente per ottenerlo.

Ho avuto un bean con ambito di sessione.

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

Ho usato il codice seguente nel mio metodo doPost () servlet.

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

ha risolto il mio problema.


Che tipo di servlet usi? compagno
Fernando Pie

1
È HttpServlet.
Anil

-1

Io uso questo:

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

E poi chiama:

MyManageBean bean = getBean(MyManageBean.class);

In questo modo puoi eseguire il refactoring del codice e monitorare gli utilizzi senza problemi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.