Obtenha o bean gerenciado JSF por nome em qualquer classe relacionada a Servlet

101

Estou tentando escrever um servlet personalizado (para AJAX / JSON) no qual gostaria de fazer referência ao meu @ManagedBeansnome. Espero mapear:

http://host/app/myBean/myProperty

para:

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

É possível carregar um bean por nome de um servlet regular? Existe um servlet JSF ou auxiliar que eu possa usar para isso?

Parece que estou estragado pela primavera, em que tudo isso é muito óbvio.

Konrad Garus
fonte
Não tenho certeza se você pode usar essas novas anotações fora do JSF / EL, mas começaria olhando para a especificação JSR 299: jcp.org/en/jsr/detail?id=299
McDowell
Outras pessoas com problemas semelhantes também podem verificar bpcatalog.dev.java.net/ajax/jsf-ajax (relacionado a AJAX e mapeamento / tratamento de solicitações, não obtendo beans por nome)
Konrad Garus

Respostas:

263

Em um artefato baseado em servlet, tais como @WebServlet, @WebFiltere @WebListener, você pode pegar um JSF "plain vanilla" @ManagedBean @RequestScopedpor:

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

e @ManagedBean @SessionScopedpor:

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

e @ManagedBean @ApplicationScopedpor:

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

Observe que isso exige que o bean já tenha sido autocreado pelo JSF de antemão. Caso contrário, eles retornarão null. Em seguida, você precisará criar manualmente o bean e usá-lo setAttribute("beanName", bean).


Se você for capaz de usar o CDI em @Namedvez do JSF 2.3 obsoleto @ManagedBean, então é ainda mais fácil, especialmente porque você não precisa mais criar manualmente os beans:

@Inject
private Bean bean;

Observe que isso não funcionará quando você estiver usando, @Named @ViewScopedporque o bean só pode ser identificado pelo estado de exibição JSF e isso só está disponível quando o FacesServletfoi chamado. Portanto, em um filtro que roda antes disso, acessar um @Injected @ViewScopedsempre será lançado ContextNotActiveException.


Somente quando você estiver dentro @ManagedBean, você pode usar @ManagedProperty:

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

Note que isso não funciona dentro de um @Namedou @WebServletou qualquer outro artefato. Ele realmente funciona @ManagedBeanapenas no interior .


Se você não estiver dentro de um @ManagedBean, mas o FacesContextestiver disponível (ou seja FacesContext#getCurrentInstance(), não retornar null), você também pode usar Application#evaluateExpressionGet():

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

que pode ser conveniente da seguinte forma:

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

e pode ser usado da seguinte forma:

Bean bean = findBean("bean");

Veja também:

BalusC
fonte
9
Sua segunda sugestão sobre apenas injetar o feijão foi tão incrivelmente simples que eu a havia esquecido totalmente. Como sempre, sua resposta é perfeita. Muito obrigado pelo seu trabalho aqui no SO.
jnt30
2
Nesse ínterim (falando do JSF 2.2), parece que o método evaluateExpressionGet foi estendido com um terceiro parâmetro que permite especificar a classe esperada para que a conversão não seja mais necessária. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);
Marc Juchli
1
@Marc: está presente desde o início. Foi apenas uma sobra de um erro de copypaste, eu acho. A resposta foi corrigida. Obrigado por notificar.
BalusC
FacesContextestá disponível mesmo que o staticmétodo do utilitário findBean()seja definido dentro de uma classe Java simples. Como ele está disponível em uma classe Java simples que não é gerenciada por JSF?
Tiny
1
@Tiny: por sua vez, é chamado por um artefato JSF dentro do mesmo thread.
BalusC de
11

Eu uso o seguinte 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);
}

Isso me permite obter o objeto retornado de uma maneira digitada.

John Yeary
fonte
3
Isso já está coberto pela resposta atualmente aceita e até de uma forma mais conveniente (o Classargumento é, nomeadamente, desnecessário neste construto).
BalusC de
3

Você tentou uma abordagem como neste link? Não tenho certeza se createValueBinding()ainda está disponível, mas um código como este deve ser acessível a partir de um Servlet antigo simples. Isso exige que ele já 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.
fonte
Isso provavelmente não funcionará em um servlet regular. O FacesContext é um artefato local de segmento por solicitação, configurado pelo ciclo de vida JSF (geralmente o FacesServlet).
McDowell
7
ValueBinding está obsoleto desde JSF 1.2 há mais de 4 anos.
BalusC
@BalusC: Mostra como estou atualizado rs. Em uma nota lateral, usar um mecanismo de busca para pesquisar técnicas está se tornando contraproducente com todas as informações antigas disponíveis. @McDowell: Isso realmente faz sentido. Vou fazer um teste só para ver o que acontece.
James P.
3

Você pode obter o bean gerenciado passando o 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;
}
Soujanya
fonte
Tento fazer isso de um servlet, mas não funciona.
Fernando Pie
0

Eu tinha o mesmo requisito.

Usei a maneira abaixo para obtê-lo.

Eu tinha um bean com escopo de sessão.

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

Usei o código abaixo em meu método doPost () de servlet.

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

resolveu meu problema.

Anil
fonte
Que tipo de servlet você usa? companheiro
Fernando Pie
1
É HttpServlet.
Anil
-1

Eu uso isso:

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 então ligue para:

MyManageBean bean = getBean(MyManageBean.class);

Desta forma, você pode refatorar seu código e rastrear usos sem problemas.

ChRoNoN
fonte