JavaServer Faces Tutorial

Esse artigo é útil por explorar os fundamentos da arquitetura do JavaServer Faces (JSF), apresentando os seus principais recursos, características, benefícios e limitações, além de, por meio de um exemplo prático, descrever como o padrão Model-View-Controller (MVC) é implementado sob essa arquitetura. Com isso, demonstramos as contribuições do JSF para o projeto de aplicações web, especialmente para a construção de camadas de visão magras e de bom desempenho. A partir do conteúdo exposto, o leitor será capaz de iniciar o desenvolvimento de aplicações web utilizando a tecnologia de referência e mais adotada do Java.

Definir a arquitetura de uma aplicação é sempre uma tarefa difícil. Deve-se levar em consideração um conjunto diverso de fatores para que o software seja eficiente, que cumpra seus objetivos, mas que também seja agradável para o usuário, não apenas visualmente, mas no que diz respeito à sua usabilidade. E também, deve ter performance adequada e utilizar os recursos disponíveis da maneira mais otimizada possível.

O projeto ainda deve ser otimizado de forma que a equipe de desenvolvedores seja capaz de cumprir suas metas de recursos e prazos, dividindo o trabalho adequadamente sem prejuízo para a qualidade do código e, consequentemente, para o produto final.

Portanto, os padrões de projeto destinados ao estabelecimento de arquiteturas de software – dentre os quais vale destacar o Model-View-Controller (MVC, ou Modelo-Visão-Controle) – são de grande importância por tornar a implementação do projeto menos desgastante, aumentado a qualidade do código e fazendo com que este alcance seus objetivos.

A arquitetura de software proposta pelo JSF para a implementação do padrão MVC tem como meta aprimorar a implementação de interfaces com o usuário, separando as representações internas de informação da camada com que o usuário da aplicação tem contato, o que diminui a necessidade de escrita de código e faz com que a camada de visão fique mais “magra”.

A característica fundamental do padrão MVC é a divisão em três camadas com responsabilidades específicas, ou seja, a camada de Modelo é responsável por representar o domínio – ou os dados – da aplicação; a camada de Visão é responsável pela mediação do contato com o usuário, normalmente através de interfaces gráficas; enquanto a camada de Controle determina o comportamento da aplicação, interpretando as ações do usuário e traduzindo suas ações.

Sendo assim, a divisão de responsabilidades entre as camadas permite a reutilização de código, escalabilidade, além da divisão do trabalho entre os membros da equipe, facilitando o desenvolvimento em paralelo, permitindo a criação de diversas visões e aumentando a produtividade. Por exemplo, uma mesma aplicação pode ter diversas visões, como vemos nas versões para dispositivos móveis de front-ends de uma aplicação web.

Sobre a plataforma Java existem diversas implementações e frameworks para o padrão MVC, dentre os quais podemos destacar o pioneiro framework Apache Struts e o Spring. É nesse contexto que está posicionado o JavaServer Faces (JSF).

Embora tenha sido idealizado quase que ao mesmo tempo que a especificação de Servlets, a especificação inicial do JSF foi publicada em 2004 com a Java Specification Request (JSR) 127 para fornecer características que correspondam ao padrão MVC complementadas por um modelo de componentes de interfaces gráficas orientado a eventos para web. Dessa forma, não é um equívoco fazer um paralelo entre a proposta do JSF para o desenvolvimento de componentes de interface baseados em eventos e a arquitetura usada há bastante tempo em aplicações desktop.

Funcionando como um framework MVC, as requisições numa aplicação JSF são atendidas por um controller único – o FacesServlet – que se responsabiliza por todas as respostas da aplicação, recebendo entradas dos usuários, validando dados, preenchendo objetos da camada de modelo e renderizando as respostas, livrando o programador da tarefa de escrever essas operações. Sendo assim, a aplicação se resume basicamente às páginas na camada de visão e JavaBeans na camada de modelo.

No exemplo apresentado nesse artigo, a camada de visão será construída em documentos XHTML, seguindo a especificação Facelets, uma linguagem para a construção de páginas baseada em templates HTML capaz de utilizar a árvore de componentes da arquitetura JSF. Facelets é a implementação recomendada para a camada de visão no JSF em detrimento de páginas JSP, por possuir performance de compilação e renderização melhor. Por serem escritos em documentos XHTML, os facelets suportam o uso de taglibs, reuso de código por meio de templates, além de serem flexíveis à customização e extensíveis.

Embora forneça uma implementação mais rígida para o padrão MVC, quando comparado com frameworks como o Struts ou Spring Framework, o JSF é mais conciso, na medida em que os eventos disparados pelo usuário na camada cliente possuem manipuladores prontos na camada servidor, reduzindo o trabalho de codificação. Por exemplo, no Struts a camada de controle é baseada em actions que precisam ser estendidas como forma de atender às requisições, executar a lógica de negócio e retornar ao Struts qual página da camada de visão deve ser renderizada. Embora não utilize classes action, o Spring MVC possui uma estrutura semelhante, na qual as classes da camada de controle interagem com o DispatcherServlet por meio de mapeamentos descritos em anotações.

Já no JSF isso não é necessário devido ao fato das respostas às requisições serem baseadas na renderização de componentes de interface, de forma semelhante ao que é feito em interfaces gráficas tradicionais, como as aplicações desktop. Esses componentes possuem um modelo de eventos que os permite reagir a mudança de valores, ações de formulário e a alterações de estado do ciclo de vida da aplicação JSF. Por isso, o JSF é considerado um framework baseado em componentes, enquanto Struts é considerado um framework baseado em ações (ou “action framework”).

Analisando a implementação das camadas, também há diferenças consideráveis entre o JSF e alguns frameworks MVC. Por exemplo, no Struts, fica restrito à camada de controle tratar as requisições por meio da execução da lógica de negócio e produzir uma resposta que ele seja capaz de traduzir em um direcionamento para a camada de visão. No entanto, no JSF, é possível misturar responsabilidades entre as camadas, o que significa que é possível implementar métodos de negócio em JavaBeans que, como parte da camada de modelo, deveriam conter apenas atributos, setters e getters. Para alguns, essa flexibilidade é um ponto positivo do JSF em relação ao Struts, enquanto para outros, isso pode levar a implementações menos elegantes no que diz respeito ao padrão MVC (ou até mesmo em relação ao paradigma de desenvolvimento orientado a objetos).

Outra comparação importante diz respeito à configuração. Tanto o Spring MVC quanto o Struts fazem uso considerável de arquivos XML, embora devamos ressaltar que a versão 2 do Struts tenha reduzido bastante essa necessidade. Por outro lado, no JSF, praticamente não há a necessidade de arquivos de configuração, o que representa uma simplificação importante, diminuindo o trabalho do desenvolvedor.

Vale ressaltar também que, enquanto Struts e Spring são frameworks, JSF é uma especificação, e por fazer parte da plataforma Java EE, existem diversas implementações do JSF, dentre as quais gostaríamos de destacar a MyFaces, criada pela Apache (veja a seção Links). No entanto, nesse artigo mostraremos a implementação de referência, ou oficial, chamada Mojarra. Para isso, vamos explorar os recursos essenciais do JSF por meio do desenvolvimento de uma aplicação web que persiste alguns dados em um banco de dados MySQL. Embora não seja o foco de nosso estudo, a camada de persistência fará uso do Hibernate ORM. Além disso, a IDE que adotamos para implementar o exemplo é o Eclipse.

Enfim, o JSF é uma alternativa interessante para a implementação do padrão MVC na medida em que diminui consideravelmente a tarefa de programação e apresenta ganhos consideráveis de desempenho na camada de visão, que não foi tão extensivamente explorada em outras implementações do padrão MVC. Além disso, seu uso em conjunto com outros frameworks é feito de maneira bastante simples, o que permite o desenvolvimento de aplicações web mais robustas e eficientes.

Instalando os pacotes necessários e criando o projeto

Considerando que foi utilizada a IDE Eclipse na implementação do projeto, vamos criar um Dynamic Web Project que chamaremos de EJMJavaServerFaces. Em seguida, vamos alterar suas propriedades para que ele inclua a API JavaServer Faces. Para isso, clique com o botão direito sobre o projeto e escolha a opção Properties. À esquerda na janela de propriedades do projeto, selecione Project Facets, marque a opção JavaServer Faces e clique no botão Ok, como mostra a Figura 1. No nosso caso, usaremos a versão 2.2. Se você não possui os pacotes do JavaServer Faces no seu classpath, é possível selecioná-los ou fazer o download na mesma tela. Se preferir, utilize gerenciadores de dependências como o Maven, especialmente porque as classes do Hibernate também precisam estar no seu classpath.

Incluindo os pacotes do JavaServer Faces na aplicação
Figura 1. Incluindo os pacotes do JavaServer Faces na aplicação.

Com essas alterações o projeto estará pronto para utilizar os recursos do JavaServer Faces, sendo, inclusive, criado o arquivo de configuração WEB-INF\faces-config.xml. Na sequência, vamos iniciar o desenvolvimento pela criação do pacote-base da aplicação. Dito isso, com o botão direito sobre a pasta Java Resources\src, no Package Explorer do Eclipse, selecione New > Package. Conforme demonstra a Figura 2, digite “br.com.devmedia.gestaoacademica” no campo Name e clique em Finish.

Criando o pacote-base da aplicação
Figura 2. Criando o pacote-base da aplicação

Para implementar a arquitetura MVC, crie dentro do pacote-base os pacotes control e model, bem como o pacote onde serão armazenadas as classes da camada de persistência da nossa aplicação (dao). Nossas páginas (a camada view) ficarão no diretório WEB-INF. Como vamos usar recursos do Hibernate para a camada de persistência, crie também o arquivo \src\hibernate.cfg.xml. Dessa forma, a estrutura do projeto deve ficar como mostrado na Figura 3.

Estrutura do projeto
Figura 3. Estrutura do projeto.

No arquivo \src\hibernate.cfg.xml manteremos as configurações de acesso ao banco de dados MySQL e o mapeamento das classes da camada de modelo no Hibernate. O código-fonte deste arquivo, no qual vemos propriedades como o endereço do banco de dados, usuário e senha, bem como o mapeamento das classes da camada de modelo, é apresentado na Listagem 1. Já o arquivo faces-config.xml foi criado automaticamente pelo Eclipse e não sofrerá alterações no nosso exemplo, mas seu código pode ser visto na Listagem 2.

Listagem 1. Código-fonte do arquivo hibernate.cfg.xml.

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 
<hibernate-configuration>
    <session-factory>    
      <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
      <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/gestaoacademica</property>
      <property name="hibernate.connection.username">gestaoacademica</property>
      <property name="hibernate.connection.password">gestaoacademica</property>
      <property name="hibernate.show_sql">true</property>
      <property name="hibernate.current_session_context_class">thread</property>             
      
<mapping class="br.com.devmedia.gestaoacademica.model.Docente" />         
    </session-factory>
</hibernate-configuration>
Listagem 2. Código-fonte do arquivo faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">
</faces-config>

Implementando as camadas de modelo e persistência

O exemplo que construiremos nesse artigo é constituído de apenas um JavaBean na camada de modelo: Docente. O mapeamento desse bean pode ser visto no nó mapping no arquivo hibernate.cfg.xml, enquanto seu código-fonte pode ser visto na Listagem 3. Nesse código, vemos anotações (vide BOX 1) do Hibernate que associam a classe Docente com sua respectiva tabela no banco de dados (através da anotação @Table). Como pode ser verificado, cada atributo da classe também é associado a um campo em sua respectiva tabela, por meio da anotação @Column.

BOX 1. Anotações

Anotações são recursos para a declaração de metadados – dados que descrevem outros dados – úteis para localizar dependências, configurações ou para fazer verificações lógicas. Essas definições serão, então, interpretadas pelo compilador para realizar uma determinada tarefa.

Listagem 3. Código-fonte da classe Docente

package br.com.devmedia.gestaoacademica.model;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name="DOCENTES")
public class Docente {
 
    @Id
    @Column(name="ID")
    @GeneratedValue
    private Integer id;
      
      @Column(name="NOME")
      private String nome;
      
      @Column(name="MATRICULA")
      private String matricula;
      
      @Column(name="TITULACAO")
      private String titulacao;
      
      public Integer getId() {
            return id;
      }
      public void setId(Integer id) {
            this.id = id;
      }
      public String getNome() {
            return nome;
      }
      public void setNome(String nome) {
            this.nome = nome;
      }
      public String getMatricula() {
            return matricula;
      }
      public void setMatricula(String matricula) {
            this.matricula = matricula;
      }
      public String getTitulacao() {
            return titulacao;
      }
      public void setTitulacao(String titulacao) {
            this.titulacao = titulacao;
      }
      
}

Com o JavaBean da camada de modelo criado, vamos implementar a camada de persistência em uma versão simplificada do padrão DAO, com uma interface (DocenteDAO) e sua implementação (DocenteDAOImpl) contendo os métodos para inserir e excluir registros do tipo Docente no banco de dados (adicionarDocente() e excluirDocente(), respectivamente), bem como recuperá-los em uma operação de listagem (listarDocentes()). O código-fonte da camada de persistência pode ser visto nas Listagens 4 e 5.

Listagem 4. Código-fonte da interface DocenteDAO.

package br.com.devmedia.gestaoacademicaweb.dao;
 
import java.util.List;
import br.com.devmedia.gestaoacademicaweb.model.Docente;
 
public interface DocenteDAO {
      
      public void adicionarDocente(Docente docente);
      public List<Docente> listarDocentes();
      public void excluirDocente(Docente docente); 
}
Listagem 5. Código-fonte da classe DocenteDAOImpl.

package br.com.devmedia.gestaoacademica.dao;
 
import java.util.List;
 
import br.com.devmedia.gestaoacademica.model.Docente;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
 
public class DocenteDAOImpl implements DocenteDAO {
 
      private static final SessionFactory sessionFactory = buildSessionFactory();
        
      public static SessionFactory buildSessionFactory(){
          try {
            return new Configuration().configure().buildSessionFactory();
          } catch (Throwable ex) {
             throw new ExceptionInInitializerError(ex);
          }
      }
        
      public static SessionFactory getSessionFactory() {
            return sessionFactory;
      }
       
      public void adicionarDocente(Docente docente) {
        Transaction trns = null;
              Session session = getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            session.save(docente);
            session.getTransaction().commit();
        } catch (RuntimeException e) {            
            if (trns != null) trns.rollback();            
        } finally {
            session.flush();
            session.close();
        }
    }
    
      public List<Docente> listarDocentes() {
            return getSessionFactory().openSession().createCriteria(Docente.class).list();         
      }
      
      
      public void excluirDocente(Docente docente) {   
        Transaction trns = null;
        Session session = getSessionFactory().openSession();
        try {
            trns = session.beginTransaction();
            session.delete(docente);
            session.getTransaction().commit();
        } catch (RuntimeException e) {            
            if (trns != null) trns.rollback();            
        } finally {
            session.flush();
            session.close();
        }   
      }
 
}

Como o uso do Hibernate não é o foco desse artigo, optamos por simplificar sua implementação deixando o método de obtenção da conexão (que usualmente é encapsulado em uma camada exclusiva ou implementado por algum framework) na própria classe DocenteDAOImpl. Dito isso, observe o método buildSessionFactory(). Este retorna uma SessionFactory (vide BOX 2) contendo a conexão com o banco que será utilizada por todos os métodos de persistência, por meio de chamadas ao método getSessionFactory(). De resto, são usados métodos do Hibernate como save(), list() e delete(), para adicionar, listar vários registros e excluir um único objeto do banco de dados, respectivamente.

BOX 2:

O conceito de SessionFactory estabelece que muitas threads podem acessar a conexão com o banco de dados de forma concorrente por meio da requisição de sessões. Um objeto do tipo SessionFactory é instanciado somente na inicialização da aplicação e, implementado por meio do padrão Singleton, pode ser acessado por outras camadas.

ManagedBeans: Implementando a camada de controle

Com JSF, a camada de controle é composta pelos chamados beans gerenciados (ou ManagedBeans), que devem ser responsáveis por manipular os dados que trafegarão pela camada de visualização da aplicação. Esses beans gerenciados são nada mais do que JavaBeans comuns, normalmente utilizados na camada de modelo das aplicações Java, mas que aplicados sobre o framework JSF servem como modelos para componentes de interface e, por isso, podem ser acessados por uma página JSF.

Embora essa prática não possa ser considerada ruim, no exemplo demonstrado nesse artigo usaremos uma forma que consideramos mais elegante de implementação para manter a camada de modelo “magra”, ou seja, apenas com seus atributos, conforme já demonstrado na Listagem 3. Sendo assim, nossa camada de controle será composta por um ManagedBean que encapsulará as operações de negócio.

Portanto, vamos criar no pacote control a classe DocenteController e, nela, os métodos excluirDocente(), listarDocente() e adicionarDocente(), responsáveis por gerenciar os três tipos possíveis de requisições disponibilizadas por nossa aplicação. Vemos o seu código na Listagem 6.

Nota:

Na versão atual do JSF não é necessário mapear o ManagedBean no arquivo faces-config.xml, uma vez que estes beans podem simplesmente ser registrados por meio de anotações.

Listagem 6. Código-fonte da classe DocenteController

package br.com.devmedia.gestaoacademica.control;
 
import java.util.List;
 
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
 
import br.com.devmedia.gestaoacademica.dao.DocenteDAO;
import br.com.devmedia.gestaoacademica.dao.DocenteDAOImpl;
import br.com.devmedia.gestaoacademica.model.Docente;
 
@ManagedBean(name="docenteController")
@SessionScoped
public class DocenteController {
 
      private Docente docente;
      private DataModel<Docente> listaDocentes;
      private String msg;
      
      public Docente getDocente() {
            return docente;
      }
      
      public void setDocente(Docente docente) {
            this.docente = docente;
      }      
      
      public DataModel<Docente> getListaDocentes() {
            
            DocenteDAO dao = new DocenteDAOImpl();
            List<Docente> lista = dao.listarDocentes();
            listaDocentes = new ListDataModel<Docente>(lista);
            return listaDocentes;
      }
 
      public void setListaDocentes(DataModel<Docente> listaDocentes) {
            this.listaDocentes = listaDocentes;
      }
 
      public String getMsg() {
            return msg;
      }
 
      public void setMsg(String msg) {
            this.msg = msg;
      }
 
      public String adicionarForm() {
             docente = new Docente();
          
          return "inserir_docente_form"; 
      }
      
      public String adicionarDocente() {
       
             DocenteDAO dao = new DocenteDAOImpl();
             dao.adicionarDocente(docente); 
          
             setMsg("Salvo com sucesso!");
             return "inserir_docente_form"; 
      }
      
      public String excluirDocente() {
            
            Docente d = (Docente)(listaDocentes.getRowData());
            DocenteDAO dao = new DocenteDAOImpl();
            dao.excluirDocente(d);
            
            setMsg("Excluído com sucesso!");
            return "listar_docentes"; 
 
      }
      
      public String listarForm() {
          
             return "listar_docentes"; 
      }
      
      
      @PostConstruct
      public void init() {
             docente = new Docente();
      }
}

A anotação @ManagedBean decora o bean, informando ao container que ele é gerenciado pelo JSF pelo nome especificado na propriedade name. Caso o nome não seja especificado, será adotado o nome da classe de forma qualificada, ou seja, com a primeira letra minúscula. Além desta, é possível definir outras propriedades como, por exemplo, o momento da inicialização do bean. Em nosso exemplo, no entanto, esses recursos não serão explorados.

Apesar disso, é necessário definir o escopo do bean, embora também seja adotado um escopo padrão caso essa propriedade não seja descrita. Em nosso exemplo, escolhemos o escopo “session”, definido por meio da anotação @SessionScoped. Isso determina que o ciclo de vida do bean se inicia com a primeira requisição HTTP e se encerra somente ao fim da sessão, ou seja, quando o usuário termina a navegação. Neste momento podemos destacar outros escopos muito utilizados, como:

  • @RequestScoped: padrão para os casos em que o escopo não seja definido explicitamente. Nesse caso, o ciclo de vida do bean se inicia quando uma requisição HTTP é feita pelo usuário e se encerra quando a resposta associada a essa requisição é finalizada;
  • @ViewScoped: com esta opção o bean vive enquanto o usuário permanecer visualizando a mesma página no navegador, ou seja, é destruído quando o usuário troca de página;
  • @ApplicationScoped: com esta opção o ciclo de vida do bean é permanente enquanto a aplicação estiver executando, ou seja, se inicia na primeira requisição HTTP que envolva o bean e é destruído quando a aplicação é encerrada.

Outro detalhe importante na nossa implementação é a inicialização do objeto que será persistido. Lembre-se que o objeto que desejamos salvar no banco de dados é do tipo Docente. Portanto, a camada de controle precisa ser capaz de acessar um objeto desse tipo e preenchê-lo com os dados que virão da camada de visão, bem como enviá-lo para a camada de persistência.

Sendo assim, declaramos em DocenteController um atributo do tipo Docente (no trecho de código private Docente docente), implementando também seu setter e seu getter. Em seguida, criamos o método init(), que instancia docente. Utilizamos nesse método a anotação @PostConstruct para fazer com que ele seja chamado logo após qualquer instanciação da classe. No nosso caso, isso garante que o método init() será chamado toda vez que DocenteController for instanciado e, dessa forma, teremos sempre um objeto Docente inicializado.

A anotação @PostConstruct também garante que o método anotado só será executado uma vez durante todo o ciclo de vida do bean gerenciado, mesmo que o bean seja instanciado várias vezes pelo container. É importante observar esse detalhe especialmente quando o bean gerenciado possui escopo de requisição (@RequestedScoped), uma vez que nesses casos seu ciclo de vida se encerra ao fim da requisição, tendo uma possibilidade maior de novas instanciações serem necessárias.

Criando a camada de visão com Facelets

Um dos recursos mais interessantes do JSF é que as interações entre os JavaBeans e a camada de visão da aplicação são implícitas, ou seja, não precisam ser implementadas. Isso é viabilizado através de um padrão de projeto intitulado Inversão de Controle (vide BOX 3), que faz com que o container seja responsável pelo mapeamento entre o JavaBean e sua camada de visão correspondente.

BOX 3. Inversão de Controle

Inversão de Controle é um padrão de projeto em que a chamada de métodos é invertida, ou seja, não é determinada pelo programador, mas delegada a um componente que possa tomar conta dessa execução – o container. Uma das maneiras de aplicar a inversão de controle é por meio da injeção de dependência, que torna o container responsável por injetar em cada componente as dependências que tiverem sido declaradas.

Como dissemos anteriormente, nossa camada de visão será baseada em Facelets, em vez de páginas JSP. A principal diferença é que o processamento de uma página JSP se dá de cima para baixo, com os elementos JSP sendo interpretados na ordem em que estão colocados na página. Em Facelets, pelo contrário, o ciclo de vida dos componentes é mais complexo, pois sua instanciação e exibição ocorre em separado e em ordem definida, o que melhora a sua performance.

Hans Bergsten, autor do livro “JavaServer Faces”, apresenta esse fato – entre outros problemas na combinação de JSP com JSF – em seu artigo “Improving JSF by Dumping JSP” (“Aprimorando JSF jogando JSP fora”, em tradução livre), indicado na seção Links.

Também é importante ressaltar que, uma vez que são implementados em documentos XHTML, os facelets utilizam um mecanismo de validação semelhante ao de documentos XML, sendo, por exemplo, sensível às diferenças entre caracteres maiúsculos e minúsculos, exigindo o fechamento de todas as tags utilizadas, etc.

No nosso exemplo, a camada de visão terá duas telas: uma para a listagem dos docentes cadastrados e outra para o cadastro de docentes, como mostram as Figuras 4 e 5. Enquanto a primeira exibe os registros cadastrados no banco, a segunda adiciona os registros quando o usuário clica no botão Salvar. Para implementá-las, vamos criar dois arquivos .xhtml no diretório \WEB-INF: listar_docentes.xhtml e inserir_docente_form.xhtml, cujos respectivos códigos-fontes podem ser vistos nas Listagens 7 e 8.

Listagem de docentes cadastrados
Figura 4. Listagem de docentes cadastrados.
Formulário de cadastro de docentes
Figura 5. Formulário de cadastro de docentes.
Listagem 7. Código-fonte da listagem de docentes.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
 
<head>
    <title>Listagem de Docentes</title>
</head>
<body>
 
<h3>Docentes Cadastrados</h3>
 
<h3>
          <h:form>
                      <h:commandLink value="+ Novo Docente" action="#{docenteController.adicionarForm}"/>
          </h:form>
</h3>
 
<h4><h:outputLabel id="oplMensagem" value="#{docenteController.msg}"/></h4>
 
<h:dataTable value="#{docenteController.listaDocentes}" var="docente" border="1">
 
          <h:column>
                      <f:facet name="header">Nome</f:facet>
                      #{docente.nome}
          </h:column>
          
          <h:column>
                      <f:facet name="header">Matricula</f:facet>
                      #{docente.matricula}
          </h:column>
          
          <h:column>
                      <f:facet name="header">Titulação</f:facet>#{docente.titulacao}
          </h:column>
          
          <h:column>
                      <f:facet name="header"> </f:facet><h:form><h:commandLink action="#{docenteController.excluirDocente}" value="Excluir"/></h:form>
          </h:column>
 
</h:dataTable>
 
</body>
</html>
Listagem 8. Código-fonte do cadastro de docentes.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
      
<h:head>    
          <title>Cadastro de Docentes</title>
</h:head>
 
<h:body>
 
<h3>Formulário de Cadastro de Docentes</h3>
 
<h3>
          <h:form>
          <h:commandLink value="Listagem de Docentes" action="#{docenteController.listarForm}"/>
          </h:form>
</h3>
 
<h4><h:outputLabel id="oplMensagem" value="#{docenteController.msg}"/></h4>
 
<h:form>
     <table>
    <tr>
        <td><h:outputText id="lnome" value="Nome:" /></td>
        <td><h:inputText id="tnome" value="#{docenteController.docente.nome}"/></td> 
    </tr>
    <tr>
        <td><h:outputText id="lmatricula" value="Matricula:" /></td>
        <td><h:inputText id="tmatricula" value="#{docenteController.docente.matricula}"/></td>
    </tr>
    <tr>
        <td><h:outputText id="ltitulacao" value="Titulação:" /></td>
        <td><h:inputText id="ttitulacao" value="#{docenteController.docente.titulacao}"/></td>
    </tr>
    <tr>
        <td colspan="2">
        <h:commandButton id="submit"  type="submit" action="#{docenteController.adicionarDocente}" value="Salvar" />
        </td>
    </tr>
</table>  
 
</h:form>
 
</h:body>
</html>

Podemos verificar no início dos códigos as primeiras diferenças entre páginas XHTML e o tradicional HTML, a começar pelas declarações XML nas primeiras linhas. Podemos verificar também as declarações das bibliotecas de tags Facelets na tag HTML, como no trecho de código xmlns:h="http://java.sun.com/jsf/html", que declara o uso da taglib h.

A declaração dos campos de formulário usa a taglib h, como no trecho de código <h:inputText id="tnome">, que cria um campo de texto. Vemos ainda outras tags sendo utilizadas, como outputText, que cria rótulos e commandButton, que cria botões, bem como form, para delimitar o formulário.

Por sua vez, a associação entre as páginas e os beans gerenciados pode ser vista nos scriptlets presentes no código. Por exemplo, vemos que a propriedade value dos campos é preenchida com valores vindos de classes na camada de controle, como demonstra o trecho de código value="#{docenteController.docente.nome}". Nesse trecho, temos uma associação do valor do campo no formulário com o atributo nome da classe Docente.

Já na listagem de docentes, cujo código pode ser visto na Listagem 7, podemos notar o uso da tag dataTable, que fornece um controle capaz de criar tabelas HTML com dados provenientes de uma Collection. Usamos esse recurso para exibir uma tabela com os docentes cadastrados no banco, que são carregados para uma List por meio do método getListaDocentes() na classe DocenteController, como pode ser visto na Listagem 6.

Outro recurso interessante é que o JSF é capaz de obter a linha em que o cursor está posicionado (por meio do método getRowData() na classe DataModel). Com isso, pudemos implementar a exclusão de registros mais facilmente, simplesmente passando o registro selecionado para o método delete() do Hibernate, como pode ser visto no método excluirDocente() na classe DocenteController.

Também observamos no código-fonte da listagem de docentes o uso da tag column, definida para criar as colunas da tabela, em que exibimos dados vindos do objeto por meio de scriptlets (como em #{docente.nome}). Além disso, usamos a tag facet para criar os cabeçalhos das colunas da tabela.

Feito isso, a aplicação está pronta e pode ser testada através da URL http://localhost:8080/GestaoAcademicaWeb/faces/listar_docentes.xhtml, que abrirá a página de listagem de docentes. Ao clicar no link Cadastrar novo Docente, somos direcionados para o formulário de cadastro. Nesta página, ao preencher os campos e clicar em Salvar, os dados inseridos são salvos no banco e a mensagem “Salvo com sucesso!” é exibida, como mostra a Figura 6.

Cadastrando Docentes
Figura 6. Cadastrando Docentes.

As mensagens de resposta das operações são carregadas no msg da classe DocenteController. Como mostram as Listagens 6 e 7, atribuímos valores à msg ao fim de cada operação, e esses valores são exibidos nas páginas por meio da tag outputText. Por exemplo, vemos na operação de exclusão que a mensagem “Excluído com sucesso!” é exibida quando um registro é excluído (vide Figura 7).

Excluindo um registro
Figura 7. Excluindo um registro.

Por fim, repare novamente na classe DocenteController que as operações de negócio retornam strings. Essas strings representam a página para a qual o usuário será direcionado ao fim da operação. Por exemplo, podemos ver que a operação excluirDocente() direciona o usuário para “listar_docentes”, exatamente como acontece em nosso teste, em que o usuário é direcionado para a listagem de docentes após excluir um registro. Da mesma forma, criamos os métodos listagem() e novoDocente(), responsáveis por criar direcionamentos para a listagem de docentes e para o cadastro de docentes, respectivamente.

Em seu livro “Ecological Interface Design” (Projeto de Interfaces Ecológicas, ainda não publicado no Brasil), Catherine Burns e John Ajdukiewicz fazem uma breve comparação entre o projeto de interfaces com o usuário e uma arte. No entanto, os próprios autores não são conclusivos quanto a essa comparação, uma vez que é bastante difícil caracterizar o projeto de interfaces como uma arte. Mais do que isso, é importante ressaltar que aspectos não somente visuais, como também de desempenho e facilidade de implementação, devem ser levados em consideração no projeto de uma interface com o usuário.

O desenvolvimento de aplicações para web ainda é um segmento importante no mercado de desenvolvimento. Logo, é natural que a comunidade Java continue buscando novas implementações para atrair profissionais, especialmente no mercado corporativo. No entanto, embora haja de fato uma farta exploração do domínio das aplicações web pela plataforma Java, é possível verificar que o desenvolvimento de interfaces para a camada de visão não teve a mesma atenção se comparado com as outras camadas do padrão de projeto mais popular, o MVC.

Por exemplo, se observarmos com atenção as implementações do padrão MVC fornecidas pelos frameworks existentes, podemos notar que há pouca inovação no que diz respeito à implementação da camada de visão, como melhorias de desempenho e qualidade, aprimoramentos no ciclo de vida dos componentes de interface, limpeza de código, aprimoramento da experiência do usuário, etc. Isso quer dizer que embora os benefícios do padrão MVC sejam bastante claros – e suas implementações ilustram de forma satisfatória esses benefícios – a camada de visão não tem sido explorada no mesmo potencial que as demais camadas pelos frameworks mais populares do mercado.

Nesse sentido, o JSF faz uma contribuição clara, por fornecer implementações simples e de boa performance para a camada de visão. Sua biblioteca de componentes de interface é bastante completa, escalável e de simples implementação, além de integrada, sendo compatível inclusive com outras implementações para o padrão MVC. Mais do que isso, o JSF fornece um mecanismo de escuta de eventos para seus componentes capaz de receber as ações do usuário, dando mais robustez à captação de eventos em aplicações web.

Embora nesse artigo tenhamos tratado da principal implementação do JSF, não exploramos bibliotecas de componentes que estendem seus recursos, dentre as quais vale destacar o PrimeFaces, bastante popular na comunidade de desenvolvedores. Lançado inicialmente em 2009, o PrimeFaces fornece diversos componentes de interface e templates compatíveis com o JSF, sendo, portanto, uma interessante oportunidade de aprofundamento para o leitor nesse tipo de tecnologia. Outra sugestão é estudar a integração do JSF com frameworks MVC como o Struts ou o Spring, além do aprofundamento na integração com o Hibernate para implementação da camada de persistência.

Dentre as características do JSF abordadas nesse artigo, podemos destacar a separação mais evidente entre o negócio e a visão em relação ao JSP, já que no código de nossos facelets só foram escritas as exibições dos componentes de interface. Isso representa um grande benefício não somente para a elegância do código produzido, mas também para a sua clareza, produzindo camadas de visão mais magras. Essa separação entre a lógica de negócio e a apresentação também traz benefícios para o projeto, na medida em que torna possível dividir as tarefas de implementação dos componentes da camada de visão entre os membros da equipe de desenvolvedores, até mesmo entre profissionais com níveis diferentes de experiência com programação.

Um exemplo para essa divisão de trabalho é a possibilidade de delegar a um grupo de desenvolvedores com menos experiência em Java a tarefa de construir a interface usando componentes de interface JSF, enquanto programadores mais experientes desenvolvem a camada server-side da aplicação, uma vez que unir essas camadas é algo simples na arquitetura JSF. No entanto, se a sua aplicação web faz um uso muito extenso de CSS ou JavaScript, além de recursos muito particulares de AJAX, talvez os componentes disponíveis no JSF não sejam suficientes, e o uso de XHTML pode trazer algumas dificuldades, fazendo com que Spring ou Struts se tornem mais atraentes, mesmo sendo necessário implementar todo o código CSS e JavaScript.

Com relação à arquitetura, a literatura especializada aponta algumas deficiências do JSF, especialmente na relação entre os ManagedBeans e a arquitetura orientada a serviços (SOA). Para alguns autores, o JSF enfraquece a SOA por não impedir que os programadores escrevam grandes quantidades de código no ManagedBean. No entanto, isso não necessariamente é um problema, uma vez que, caso o desenvolvedor realmente queira seguir a arquitetura orientada a serviços, basta implementar a lógica de negócio de acordo com os padrões definidos para a mesma.

Há também críticas à arquitetura de navegação fornecida no JSF, que embora traga alguns aprimoramentos em relação ao Struts, não permite, por exemplo, implementar redirecionamentos por meio de configurações, já que as actions são configuradas na página. Também não possui recursos nativos para o gerenciamento do fluxo de páginas, embora seja compatível com o Spring Web Flow, módulo do Spring framework que adiciona ao MVC a possibilidade de encapsular uma sequência de passos para guiar o usuário através de um conjunto definido de páginas que representem uma tarefa específica relacionada ao negócio da aplicação.

Por fim, podemos afirmar que o JavaServer Faces representa uma arquitetura que enriquece a plataforma Java, fornecendo implementações bastante interessantes para o gerenciamento de dados em componentes de interface, manutenção do estado de componentes, validação de dados de usuários e na captura de eventos em aplicações web.


Você pode gostar
  • O que são testes unitários?:
    Os testes unitários procuram aferir a corretude do código, em sua menor fração. Em linguagens orientadas a objetos, essa menor parte do código pode ser um método de uma classe.
  • Conheça o MySQL Fabric:
    Neste artigo iremos conhecer e entender o que é o MySQL Fabric e em qual situações podemos utilizá-lo. Também falaremos sobre as melhores práticas na configuração, instalação e monitoramento do MySQL com o MySQL Fabric.
  • O que é Neo4j?:
    Neste curso você conhecerá o Neo4j, um banco de dados NoSQL que implementa o modelo de grafos, uma estrutura de dados composta por nós (vértices) que estão ligados por meio de relacionamentos (arestas).

Saiba mais sobre JavaServer Faces ;)