Obtenga el bean administrado JSF por nombre en cualquier clase relacionada con Servlet

101

Estoy tratando de escribir un servlet personalizado (para AJAX / JSON) en el que me gustaría hacer referencia a mi @ManagedBeansnombre. Espero mapear:

http://host/app/myBean/myProperty

a:

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

¿Es posible cargar un bean por su nombre desde un servlet normal? ¿Hay un servlet JSF o un ayudante que pueda usar?

Parece que me echó a perder la primavera en la que todo esto es demasiado obvio.

Konrad Garus
fuente
No estoy seguro de si puede usar estas nuevas anotaciones fuera de JSF / EL, pero comenzaría por mirar la especificación JSR 299: jcp.org/en/jsr/detail?id=299
McDowell
Otras personas que tienen problemas con problemas similares también pueden consultar bpcatalog.dev.java.net/ajax/jsf-ajax (relacionado con AJAX y solicitar mapeo / manejo, sin obtener beans por nombre)
Konrad Garus

Respuestas:

263

En un artefacto basado en servlet, como @WebServlet, @WebFiltery @WebListener, puede obtener un JSF "simple" @ManagedBean @RequestScopedde la siguiente manera:

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

y @ManagedBean @SessionScopedpor:

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

y @ManagedBean @ApplicationScopedpor:

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

Tenga en cuenta que esto requiere que el bean ya sea creado automáticamente por JSF de antemano. De lo contrario, estos volverán null. Luego, necesitaría crear manualmente el bean y usar setAttribute("beanName", bean).


Si puede usar CDI en @Namedlugar de JSF 2.3 en desuso @ManagedBean, entonces es aún más fácil, particularmente porque ya no necesita crear manualmente los beans:

@Inject
private Bean bean;

Tenga en cuenta que esto no funcionará cuando esté usando @Named @ViewScopedporque el bean solo puede ser identificado por el estado de vista JSF y eso solo está disponible cuando FacesServletse ha invocado. Entonces, en un filtro que se ejecuta antes de eso, acceder a un @Injected @ViewScopedsiempre arrojará ContextNotActiveException.


Solo cuando estés dentro @ManagedBean, puedes usar @ManagedProperty:

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

Tenga en cuenta que esto no funciona dentro de una @Namedo @WebServleto cualquier otro artefacto. Realmente funciona @ManagedBeansolo por dentro .


Si no está dentro de a @ManagedBean, pero FacesContextestá disponible (es decir FacesContext#getCurrentInstance(), no regresa null), también puede usar Application#evaluateExpressionGet():

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

que puede ser conveniente de la siguiente manera:

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

y se puede utilizar de la siguiente manera:

Bean bean = findBean("bean");

Ver también:

BalusC
fuente
9
Tu segunda sugerencia sobre simplemente inyectar el frijol fue tan increíblemente simple que la había pasado por alto por completo. Como siempre, su respuesta va perfectamente al grano. Muchas gracias por su trabajo aquí en SO.
jnt30
2
Mientras tanto (hablando a partir de JSF 2.2) parece que el método evaluabaExpressionGet se extendió con un tercer parámetro que permite especificar la clase esperada para que la conversión ya no sea necesaria. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli
1
@Marc: Ha estado desde el principio. Supongo que fue solo un sobrante de un error de copiar y pegar. La respuesta ha sido corregida. Gracias por notificarnos.
BalusC
FacesContextestá disponible aunque el staticmétodo de utilidad findBean()esté definido dentro de una clase Java simple. ¿Cómo está disponible allí en una clase Java simple que no está administrada por JSF?
Diminuto
1
@Tiny: a su vez, es llamado por un artefacto JSF dentro del mismo hilo.
BalusC
11

Utilizo el siguiente método:

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

Esto me permite obtener el objeto devuelto por escrito.

John Yeary
fuente
3
Esto ya está cubierto por la respuesta actualmente aceptada e incluso de una manera más conveniente (el Classargumento es innecesario en esta construcción).
BalusC
3

¿Has probado un enfoque como el de este enlace? No estoy seguro de si createValueBinding()todavía está disponible, pero un código como este debería ser accesible desde un Servlet antiguo. Esto requiere que el bean ya exista.

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.
fuente
Es probable que esto no funcione en un servlet normal. FacesContext es un artefacto local de subproceso por solicitud configurado por el ciclo de vida de JSF (generalmente el FacesServlet).
McDowell
7
ValueBinding está obsoleto desde JSF 1.2 hace más de 4 años.
BalusC
@BalusC: Muestra lo actualizado que estoy lol. En una nota al margen, utilizar un motor de búsqueda para investigar técnicas está resultando contraproducente con toda la información antigua que existe. @McDowell: Eso tiene sentido. Haré una prueba solo para ver qué pasa.
James P.
3

Puede obtener el bean administrado pasando el nombre:

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
fuente
Intento hacerlo desde un servlet pero no funciona.
Fernando Pie
0

Tenía el mismo requisito.

He utilizado la siguiente forma para obtenerlo.

Tenía bean con alcance de sesión.

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

He usado el siguiente código en mi método servlet doPost ().

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

resolvió mi problema.

Añil
fuente
¿Qué tipo de servlet usas? mate
Fernando Pie
1
Es HttpServlet.
Anil
-1

Yo uso esto:

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

Y luego llama:

MyManageBean bean = getBean(MyManageBean.class);

De esta manera, puede refactorizar su código y realizar un seguimiento de los usos sin problemas.

CRONO
fuente