Implementação de CRUD em Java - Parte II - Servlets e classes base
Já falamos sobre o MVC e como ele se aplica no exercício de nosso grande professor Reinaldo.
Agora vamos ver como tudo isso funciona lá nas linhas de código. Começaremos pelas classes e servlets que são a base de nossa aplicação.
Vamos seguir o fluxo normal da aplicação. Como exemplo, usaremos o cadastro de municípios. As classes BusinessLogic e ControllerServlet são comuns ao sistema, ou seja, mesmo se adicionarmos mais controladores, modelos, JSPs, ..., elas vão permanecer do mesmo jeito.
index.jsp
O index.jsp é bastante simples e pode ser chamado como o menu de nossa aplicação. Nele só contém links para diversas tarefas que podemos fazer.
Por exemplo:
<a href="ControllerServlet?business=MunicipioController&action=novo">Cadastrar Municipio</a><br>
Aqui criamos uma âncora com um endereço alvo, ou simplesmente, um link. Definimos o texto que aparecerá na tela como “Cadastrar Municipio”. No atributo href, definimos o endereço alvo:
ControllerServlet?business=MunicipioController&action=novo
O que isso quer dizer?
Chame o servlet ControllerServlet e passe os seguintes parâmetros para ele: “business”, valendo “MunicipioController” e “action” valendo “novo”.

Estes parâmetros definem a rota da aplicação. O que fazer e quem fará. Se não tivéssemos colocado assim, deveríamos ter um Servlet para cada possível ação e cada modelo. Já imaginou termos MunicipioControllerNovo, MunicipioControllerListar, MunicipioControllerSalvar, ... e a mesma coisa para todos os outros modelos? Ruim né.
BusinessLogic.java
Como dito anteriormente, ela contém apenas um método abstrato, ou seja, especifica que ele não existe aqui, apenas sua assinatura; e que todos as classes que implementem BusinessLogic, DEVEM implementar (de verdade!) este método.
Ele garante que teremos um padrão entre todos os controladores do sistema, assim o ControllerServlet consigará repassar a requisição corretamente. É uma convenção que adotamos.
public interface BusinessLogic {
public abstract void execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Neste momento criamos uma interface. Temos apenas o método “execute” que é abstrato. Desse modo, todas as classes filhas de BusinessLogic, ou ainda, todos os controladores do sistema, devem ter este método e implementá-lo de verdade, pois para cada controlador, o comportamento é diferente. Um controlador de Pessoa faz coisas diferentes de um controlador de Município, por exemplo.
O parâmetros que o método recebe são os objetos correspondentes da sessão atual. O “request” contém todas as informações da requisição. Entendemos por requisição o pedido do usuário ao sistema, por exemplo, no momento que ele clica em “Gravar” em formulário de cadastro, é solicitado ao sistema que grave os dados no banco de dados.
No caso do formulário, é neste objeto que estará os dados que o usuário preencheu, pois pertencem à requisição.
O “response”, pode ser dizer que é o objeto de saída. Ele representa a porta de comunicação entre o sistema e o usuário. Depois do cadastro feito, devemos falar algo ao usuário, como “Sucesso” ou “Falha”. Este objeto é justamente o lugar que mandaremos informações de volta ao usuário.
Estes 2 objetos são tudo que um controlador precisa para trabalhar. Nele conterá os comandos do que ele deve fazer, bem como os dados enviados pelo navegador do usuário. E, após finalizar, um lugar para mostrar o resultado.
ControllerServlet.java
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...
}
O método processRequest é o ponto de entrada do Servlet. Na verdade, existem 2 métodos específicos para cada tipo de solicitação. Lembram que podemos chamar um servlet passando os parâmetros por GET ou POST? Aqui podemos tratar os 2 separadamente, se quisermos. Por padrão, ambos remetem ao processRequest, por isso não faz diferença qual você usa, todos vão cair aqui.
Antes de prosseguir no ControllerServlet, vamos dar uma revisada no try, catch e finally, pois existem vários neste servlet.
As palavras reservadas try, catch e finally são usadas para tratar exceções no seu código. Exceção? Sim, um erro, falha.
Geralmente, quando algo dá errado no código, somos presenteados com uma página em branco. Bem, não era o que esperávamos, disso sabemos. Mas então, o que aconteceu? Aonde está o erro? Os blocos try servem para isso.
Quando escrevemos algum código potencialmente perigoso, ou seja, que pode falhar em alguma circunstância, devemos informar ao compilador/interpretador deste fato, para que ele possa “observar” as operações, e caso dê algo errado, te “avisar” ao invés de despencar. Informamos colocando este código dentro de um bloco try.
Vários tipos de erros podem ocorrer, como tentar acessar uma variável nula, converter um texto inválido para um número, etc... No bloco try, usamos a palavra catch para identificar os blocos de código que serão executados caso algo dê errado. Melhor ainda, podemos separar estes blocos de código por tipo de erro, assim saberemos que erro ocorreu e teremos a chance de informar ao usuário (ou ao desenvolvedor) o que aconteceu exatamente.
O finally representa um bloco que deve SEMPRE ser executado, com ou sem erro, e somente após tudo já tiver sido feito. Digamos que é onde colocamos nosso código de “limpeza”, ou seja, o responsável por limpar as variáveis, fechar conexões, liberar memória (não necessário em Java), etc...
try {
// código que pode causar um erro
} catch ( /* tipo de erro que este bloco pega */ ) {
// código para tratar erros do tipo especificado acima.
} finally {
// código de limpeza, que será executado após os blocos
// acima finalizarem.
}
Todas as exceções herdam da classe Exception. Por isso elas compartilham atributos e métodos.
Posso tratar vários tipos de erros? Sim, apenas use mais blocos catch(), cada um pegando a exceção desejada.
Não quero tratar as exceções separadamente, apenas pegar todas em um lugar só: pegue no bloco catch a exceção do tipo “Exception” que é a classe mãe, por isso, ela pode representar todas as outras.
Ex:
try {
// código que pode causar um erro
} catch (Exception e) {
// qualquer erro que aconteça, cairá aqui.
}
O “e” é uma variável local ao bloco, que contém as informações do erro, como descrição, tipo, o stack trace,.. O Stack Trace é uma pilha de chamada, espécie de um histórico de metódos, que mostra qual metódo chamou o outro, e assim por diante, até chegar no momento do erro.
Posso, intencionalmente causar uma exceção quando sei que algo deu errado? Sim, use a palavra reservada throw. Sua traduação é, literalmente, “jogar”. Ou seja, você joga a exceção e o catch “pega”. :)
No momento de jogar a exceção, especifique qual o tipo de exceção, instanciando um objeto dela. Exemplo em um código de divisão, onde sabemos que não podemos dividir por zero:
try {
int a = 1;
int b = 0;
if(b <= 0)
throw new ArithmeticException();
return a / b;
} catch (ArithmeticException e) {
System.out.println(“Tentativa de dividir por zero”);
}
Voltando ao ControllerServlet...
response.setContentType("text/html;charset=UTF-8");
Esta linha apenas define o formato; a codificação dos caracteres na saída da página. Algumas codificações não tem todos os caracteres de todas as línguas. O UTF-8 é uma codificação Unicode e pode representar todos os caracteres (dependendo de seu tamanho) de todos os alfabetos, por isso é recomendado usá-lo. De português e inglês a chinês e japonês. :)
Então:
String businessLogicClassName = request.getParameter("business");
Class businessLogicClass = null;
try {
businessLogicClass = Class.forName("br.com.delta.controller." + businessLogicClassName);
} catch (ClassNotFoundException e) {
throw new ServletException("Não encontrou a classe" + businessLogicClassName);
}
A primeira coisa que fazemos aqui, é saber qual o controlador alvo que foi solicitado.
String businessLogicClassName = request.getParameter("business");
O controlador alvo veio em business, e foi passado pelo index.jsp, como mostrado acima.
Em nossa simulação, estamos trabalhando com municípios, então o controlador passado foi o “MunicipioController”. Desse modo, a variável “businessLogicClassName” vale a String “MunicipioController”.
Mas só temos o nome do controlador, e não nos serve apenas o nome. Afinal, para que eu use esta classe/controlador, tenho que instanciá-lo. Se eu fizesse manualmente, ficaria:
MunicipioController mc = new MunicipioController();
Porém, MunicipioController é o nome da classe, o Java sabe disso. Mas “ MunicipioController” é apenas um texto, e o Java não tem nenhum interesse nele nem sabe o que significa. Uma coisa é trabalharmos com uma Class, outra com String.
A linguagem fornece meios de buscar classes em sua aplicação através do nome. Antes de tentarmos instanciar esta classe então, é prudente verificarmos se ela ao menos existe. Assim...
Class businessLogicClass = null;
try {
businessLogicClass = Class.forName("br.com.delta.controller." + businessLogicClassName);
} catch (ClassNotFoundException e) {
throw new ServletException("Não encontrou a classe" + businessLogicClassName);
}
Começamos com a variável businessLogicClass em branco. Ela será usada para guardar a classe do controlador, caso encontrada.
Usamos então o método forName, que busca a classe desejada apenas pelo seu nome. Observe a concatenação que ocorre em "br.com.delta.controller." + businessLogicClassName.
Nosso “businessLogicClassName” vale “MunicipioController”, que foi atribuído logo acima. Como nossas classes estão separadas em pacotes, devemos informar o caminho completo ao forName. Então juntamos o caminho e nome do pacote aonde nossos controladores residem com o nome do controlador. No final, ficaria:
"br.com.delta.controller." + businessLogicClassName
"br.com.delta.controller." + “MunicipioController”
"br.com.delta.controller.MunicipioController”
Chegamos com sucesso à nossa classe. :)
Note que a classe será atribuída à businessLogicClass. Note também, que a classe pode não existir. Neste caso, o método forName “levantará” ou “jogará” uma exceção, que o catch abaixo conseguirá pegar tranqüilamente, pois nosso código está dentro do try. Viu que no catch() temos a exceção “ClassNotFoundException”, por isso este catch só trata exceções deste tipo, que é justamente a apropriada caso a classe não seja encontrada. (Class Not Found).
Prosseguindo. Se continuamos até aqui, é porque a classe foi encontrada. Então, ao próximo trecho:
if (!BusinessLogic.class.isAssignableFrom(businessLogicClass)) {
throw new ServletException("Classe não Implementa a Interface: " + businessLogicClassName);
}
A classe pode até existir, mas ainda não sabemos se ela está no formato adequado de um controlador. Afinal, fizemos o BusinessLogic justamente para isso: garantir uma interface de comunicação padrão entre os controladores. Então, verificamos se o controlador solicitado realmente implementa o BusinessLogic.
Se não, ele joga uma exceção do tipo ServletException com a mensagem “Classe não Implementa a Interface: MunicipioController”.
Próximo trecho:
BusinessLogic businessLogicObject = null;
try {
businessLogicObject = (BusinessLogic) businessLogicClass.newInstance();
} catch (InstantiationException e) {
throw new ServletException(e);
} catch (IllegalAccessException e) {
throw new ServletException(e);
}
Até então já fizemos todas as verificações necessárias e temos certeza que o controlador alvo existe e é válido (implementa BusinessLogic).
Agora basta instanciarmos e chamá-lo para fazer o trabalho de verdade.
Começamos com a variável “businessLogicObject” vazia, e tentamos instanciá-la através do método newInstance(). Note que, independemente do controlador alvo, guardaremos ele em uma variável do tipo BusinessLogic, afinal, não sabemos que tipo de controlador é de antemão. Por isso precisamos algo mais “genérico”. Polimorfismo, alguém? :)
Observem os 2 catchs. Qualquer erro dos tipos descritos nos parâmetros serão pegos. Se acontecer, note que a exceção é jogada novamente com o throw. Espere, mas já estamos dentro de um catch, como vou jogar a exceção novamente? Ela não vai cair dentro do mesmo catch que tentará pegar a exceção? Neste caso não, o Java invoca o catch de nível imediatamente superior, como mostrado lá em cima.
Já temos a instância, agora invocamos o método responsável:
try {
businessLogicObject.execute(request, response);
} catch (Exception e) {
throw new ServletException("A lógica de negócios causou uma exceção", e);
}
businessLogicObject já contém uma instância de nosso controlador específico. Basta agora, chamarmos o método de “serviço”, ou seja, aquele que saberá o que fazer e o fará! Para que ele saiba disso tudo, passamos os objetos request e response. É tudo o que ele precisará, conforme explicado lá em cima no BusinessLogic.java.
Viram que o ControllerServlet só usou o parâmetro “business” de toda a requisição. E é essa sua função: verificar o controlador alvo e repassar os dados. Isso justifica porque ele é o responsável pelo despacho de informações.
Na parte III veremos sobre os controlador específicos e a rota que uma solicitação toma dentro dele, de acordo com a escolha do usuário.
- Blog do Fausto
- Se logue ou se registre para poder enviar comentários

Implementação de CRUD em Java
Excelente série de artigos. Prática e eficaz. Parabéns pelo conteúdo.