CDI Managed Beans
Um dos aspectos fundamentais a considerar no desenho e arquitetura de uma aplicação web é a clara separação entre o font-end e back-end.
De lembrar que o front-end é responsável por receber a entrada em várias formas do usuário e processá-la para adequá-la a uma especificação útil para o back-end.
O front-end diz respeito à camada de apresentação, às paginas.
O back-end compreende as regras de negócios, o acesso aos dados e os métodos de operação sobre eles. As APIs de persistência também podem ser compreendidas no backend.
Para esta separação a tecnologia JavaServer Faces utiliza os Beans. As paginas JSF ligam-se diretamente à propriedades do bean e toda a logica do programa é definida no código de implementação do bean.
O que é um Bean?
Na especificação a eles relativos, os Beans ou Javabeans são definidos como “ componentes reutilizáveis de software que podem ser manipulados visualmente com uma ferramente da desenvolvimento”. Em termos mais práticos, o Bean é uma classe Java que expõe as suas propriedades, seguindo algumas convenções precisas. Os Beans são utilizados para encapsular vários objetos num único objeto, o bean. Sendo assim possível tratar estes objetos como um único objecto em vez de vários objetos individuais.
É importante não confundir os JavaBeans com os Enterprise Java Beans(EJBs), que são componentes utilizados e utilizáveis apenas em servidores de aplicações e que fazem parte a especificação Java EE.
O que distingue um JavaBeans de uma classe java?
Qualquer classe Java que adere a certas convenções relativas à definição das suas propriedades e métodos pode ser um JavaBean.
As convecções definem que a classe:
- implemente a interface Serializable do pacote java.io, que possibilita a persistência e a restauração do estado do objecto;
- possua um construtor canônico, ou seja, um construtor sem argumentos;
que as suas propriedades sejam acessíveis através dos métodos “get” e “set”, seguindo o padrão de nomenclatura CamelCase;
pode conter qualquer método de tratamento de eventos.
Managed Beans
No contexto da tecnologia JavaServer Faces entram-se objetos chamados beans mas que aqui ganham uma conceituação e papel algo diferente dos clássicos beans e são chamados Managed Beans, ou seja, beans geridos.
Os Managed Beans ou beans geridos pelo container ganham uma nova conceituação da especificação JavaEE e eram até agora, sobretudo, utilizados em aplicações JavaServer Faces. Hoje podem ser utilizados em qualquer lugar numa aplicação JavaEE, e não apenas nos módulos Web. São classes Java que herdam as propriedades dos JavaBeans e fazem ponte entre a parte visual, front-end, e a parte da camada de negócios, ou seja o back-end da aplicação. Normalmente, um Managed Bean é utilizado por outros componentes da aplicação no processo de interação com as paginas.
Um managed bean pode ser definido com a anotação @ManagedBean do pacote javax.faces.bean Esta notação passou a ser utilizada a partir da versão 2.0 do JSF. Nas versões atuais é uma anotação ainda suportada mas já marcada para ser deprecated.
Utilizando a anotação @ManagedBean, por padrão, o JSF assume que o nome do managed bean é o nome da classe com a primeira letra minúscula e o escopo request é assumido como padrão. Nom entanto o nome do managed bean pode ser especificado com a propriedade _name _da anotação @ManagedBean definida da seguinte forma: @ManagedBeam(name =”funcionario”).
As boas práticas orientam no sentido de as paginas conterem apenas a parte visual da aplicação, deixando aos Managed Beans a lógica da apresentação.
Os Managed Beans, por sua vez, devem conter somente a lógica da apresentação, deixando a parte do negócio às classes específicas do backe-end que podem compreender JavaBeans, DAOs, Entidades JPA, EJBs, etc.
As páginas acedem e se comunicam com os Managed Beans através das expressões EL (Expression Language), que são expressões embutidas nas paginas XHTML na seguinte forma: #{ ...}.
Exemplo
Neste extrato de código pode-se ver como através da expressão #{usuarioMBean.usuario.nomeUsurario} a pagina XHTML acede à propriedade nomeUsurario do objeto usuario declarado no Managed Bean com o nome usuarioMBean.
Named Beans
Atualmente são dois o tipos de Beans que pode interagir com as paginas JSF: Os JSF Managed bens e os CDI named beans. Os JSF Managed Beans, anteriorimente explicados só podem ser utilizados no contexto de uma aplicação JSF e brevemente serão deprecated. Os CDI named beans foram introduzidos com a platataforma Java EE 6 e podem integrar-se naturalmente com outras APIs da plataforma Java EE como por exemplo dos EJBs(Enterprise JavaBeans). Os CDI Named Beans são recomendados sobre os JSF managed beans.
Para tornar uma classe Java num CDI named bean é necessário apenas que a classe tenha um construtor publico sem argumento(um é sempre criado implicitamente se não for declarado nenhum construtor, como é padrão no Java) com a anotação @Named ao nível da definição da classe. O extrato de código a seguir é um exemplo da definição de um CDI Managed bean.
...
@Named(value = "participanteMBean")
@RequestScoped
public class ParticipanteMBean implements Serializable{
private static final long serialVersionUID = 1L;
private Participante participante;
private List<Sexo> sexos;
private MunicipioDAO municipioDAO;
private ParticipanteDAO participanteDAO;
private List<Municipio> municipios;
private List<Participante> participantes;
public ParticipanteMBean() {
}
@PostConstruct
public void inicializar() {
participante = new Participante();
sexos = Arrays.asList(Sexo.values());
municipioDAO = new MunicipioDAO();
participanteDAO = new ParticipanteDAO();
}
...
A anotação @Named indica que o bean em questão é um CDI named bean gerido pelo contexto do CDI. Esta anotação tem um atributo opcional value que pode ser utilizado para definir o nome lógico pelo qual o bean pode ser acedido pelas paginas JSF através da EL (Expression Language). Por convenção o valor para este atributo é o nome da classe com a primeira letra minúscula. Quando este atributo não é definido especificamente o gestor de contexto define o nome do bean segundo o padrão da convenção. Ou seja, o nome da classe com a primeira letra minúscula.
O CDI (Contexts and Dependency Injection) para a plataforma Java EE, define um conjunto de seriços contextuais aos componentes de negócio, fornecidos pleo container Java EE, que tornam mais fácil o desenvolvimento de aplicações Web que queiram integrar a tecnologia JavaServer Faces com a tecnologia dos EJBs. Inicialmente projetado para uso com objetos stateful, o CDI passou a prover uma utilização mais ampla, permitindo a interação entre vários tipos de componentes de uma maneira flexível e segura.
Escopo dos Beans
Na sua definição um CDI Managed Bean tem um nome e um escopo.
Os exemplos de beans visto até aqui utilizam anotações de tipo de escopo. O escopo de um bean determina o ciclo e o tempo de vida das instâncias do bean. O escopo também determina que clientes se referem a quais instâncias do bean. De acordo com a especificação CDI, um escopo determina:
Quando uma nova instância de qualquer bean com esse escopo é criada
Quando uma instância existente de qualquer bean com esse escopo é destruída
Quais referências injetadas referem-se a qualquer instância de um bean com esse escopo
Este tempo de vida afeta principalmente a durabilidade dos dados que a instância armazena.Por isto é necessário escolher o escopo a utilizar em cada managed bean em função dos requisitos
Por exemplo, se temos um bean com escopo de sessão Funcionario, todos os beans que são chamados no contexto do mesmo HttpSession verão a mesma instância de Funcionario. Essa instância será criada automaticamente na primeira vez que um Funcionário for necessário nessa sessão, e será automaticamente destruída quando a sessão terminar.
O CDI possui quatro escopos pré-definidos, disponíveis no pacote javax.enterprise.context:
@RequestScoped
@SessionScoped
@ApplicationScoped
@ConversationScoped
Para uma aplicação web que utiliza CDI:
qualquer requisição servlet tem acesso aos escopos de requisição, sessão e aplicação ativos, e, adicionalmente
qualquer requisição JSF tem acesso ao escopo de conversação ativo.
Uma extensão do CDI pode implementar o suporte para o escopo de conversação noutros outros frameworks web.
Os escopos de requisição e aplicação também estão disponíveis:
durante invocações de métodos remotos de EJB,
durante invocações de métodos assíncronos de EJB,
durante timeouts de EJB,
durante a entrega de mensagem a um message-driven bean,
durante a entrega de mensagem a um MessageListener, e
durante a invocação de um web service
Se a aplicação tentar invocar um bean com um escopo que não possui um contexto ativo, uma ContextNotActiveException é lançada pelo contêiner em tempo de execução.
Os managed beans com escopo @SessionScoped ou @ConversationScoped devem ser serializáveis, uma vez que o contêiner mantém a sessão HTTP resguardada ao longo do tempo.
A seguir é apresentado um quadro descritivo dos escopos pré-definidos do CDI.
Escopo | Anotação | Descrição |
---|---|---|
Request | @RequestScoped | Com o escopo Request a instancia do bean é criado quando uma requisição HTTP ocorre e é destruído assim que a resposta à requisição for enviada ao cliente. As instancias dos beans são compartilhadas durante toda a duração de uma única requisição. Este escopo é pratico para situações em que disponibilidade das informações é necessária por pouco tempo, apenas durante uma determinada requisição |
Session | @SessionScoped | Com o escopo Session a instancia do bean é criada na primeira requisição HTTP e só é destruída quando a sessão HTTP for invalidada. O bean vive durante todo o tempo em que a sessão HTTP durar. Cada utilizador da aplicação tem obtém a sua própria instancia de um bean de sessão e esta instancia fica armazenada na sessão do utilizador que fez a requisição. |
Application | @ApplicationScoped | Com o escopo Application a instancia do bean é criada durante a primeira requisição HTTP que fizer referencia a este bean e apenas é destruído quando a aplicação Web for encerrada. As instancias destes beans existem durante todo o ciclo de vida da aplicação. Um bean com escopo Application é compartilhado por todas as sessões de utilizadores. |
Conversation | @ConversationScoped | O escopo de conversação pode estender-se por várias requisições, mas é tipicamente menor que o escopo de sessão. As instancias dos beans com este escopo são compartilhadas através das várias requisições e existem durante o período de conversação definido pelo programador. Os métodos begin() e end() delimitam a conversação. |
Dependent | @Dependent | É o escopo padrão do CDI, se nenhum for especificado. As instancias dos beans com o escopo dependente não são compartilhados. Cada vez que um bean for injetado, é criada uma nova instância que mantém o mesmo ciclo de vida do bem que o injetou. |
Flow | @FlowScoped | O Flow é um escopo do CDI que define o escopo de um bean dentro do fluxon de uma aplicação Web. |
Os escopos mais comumente utilizados nas aplicações Web construídas com a tecnologia JavaServer Faces são o escopo Request, Session e Aplication.
Escopo View
O escopo com a anotação @ViewScoped é da API do JavaServer Faces e está disponível no pacote javax.faces.view. e é compatível com o CDI.
Com este escopo, o bean permanece disponível enquanto o utilizador interagir com a mesma pagina JSF na janela ou separador do browser. O bean é criado durante uma requisição HTTP e destruído quando o utilizador mudar de pagina. O Bean deve implementar a interface java.io.Serializable.
Quando se utiliza este escopo com o CDI é preciso prestar atenção ao pacote a importar. O mesmo se diga das demais anotações dos escopos CDI. Boa parte dos escopos do CDI possuem semelhantes do pacote javax.faces.bean.
Métodos de controlo do ciclo de vida do bean
@PostConstruct e @PreDestroy
A anotação PostConstruct é utilizada num método que dever ser executado depois da injeção de dependência. É implementado para executar qualquer inicialização.
Um método anotado com PostConstruct será invocado a cada requisição, uma vez que cada requisição requer uma instância separada do escopo da requisição do bean.
A anotação PreDestroy é utilizada em métodos como um callback para notificar que a instancia está no processo que levará à sua remoção pelo container.
Um método anotado com PreDestroy é normalmente utilizado para libertar os recursos anteriormente disponibilizados.
Os métodos com estas anotações são invocados automaticamente pelo container.
O @PostConstruct assim que o bem é criado e o PreDestroy quando o bena está prestes a ser destruído.
A seguir é apresentado o código de um CDI Bean mais complexo em relação aos exemplos anteriores. Alguns aspetos do bean serão esclarecidos a medida se se for avançando com a leitura.
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.event.ActionEvent;
import org.ao.mirangol.dao.ProjetoDAO;
import org.ao.mirangol.dao.TipoProjetoDAO;
import org.ao.mirangol.modelo.Projeto;
import org.ao.mirangol.modelo.TipoProjeto;
@Named(value = "projetoMBean")
@RequestScoped
public class ProjetoMBean {
private Projeto projeto;
private ProjetoDAO projetoDAO;
private TipoProjetoDAO tipoProjetoDAO;
private List<Projeto> projetos;
private List<TipoProjeto> tipoProjetos;
public ProjetoMBean() {
}
@PostConstruct
public void inicializar() {
projeto = new Projeto();
projetoDAO = new ProjetoDAO();
tipoProjetoDAO = new TipoProjetoDAO();
}
public Projeto getProjeto() {
return projeto;
}
public void setProjeto(Projeto projeto) {
this.projeto = projeto;
}
public ProjetoDAO getProjetoDAO() {
return projetoDAO;
}
public void setProjetoDAO(ProjetoDAO projetoDAO) {
this.projetoDAO = projetoDAO;
}
public String newSave() {
projeto = new Projeto();
return "paginas/novoprojeto";
}
public void save(ActionEvent event) {
projetoDAO.save(projeto);
projeto = new Projeto();
}
public List<Projeto> getListaProjetos() {
if (projetos == null) {
projetos = projetoDAO.findAll();
}
return projetos;
}
public String startEdit() {
return "/paginas/editarprojeto";
}
public String edit() {
projetoDAO.update(projeto);
projetos = null;
return "listaprojetos";
}
public String delete() {
projetoDAO.delete(projeto);
projetos = null;
return "listaprojetos";
}
public List<TipoProjeto> getListaTipoProjetos() {
if (tipoProjetos == null) {
tipoProjetos = tipoProjetoDAO.findAll();
}
return tipoProjetos;
}
}
Prática
Vistos que estão os conceitos teóricos, recomenda-se a pratica implementado os demais CDI Beans do projeto em construção ao longo do eBook.