jan

31

Spring Framework Parte 5 -> Spring Web MVC

By Felipe Saab

Antes de entrar no assunto deixe-me dar aquele aviso de praxe: caso você não ainda não conheça o básico do Spring Framework dê uma olhadinha nos posts anteriores dessa série:



Beleza, já que você conhece o Spring vamos ao assunto de hoje: como o Spring implementa o padrão de projeto Model-View-Controller (MVC).

O MVC é um padrão de arquitetura de software que visa separar a lógica de negócio da lógica de apresentação, permitindo o desenvolvimento, teste e manutenção isolado de ambos (via Wikipedia).

A imagem acima descreve o fluxo das informações no padrão MVC:

1 – O usuário dispara um evento através da camada de visualização (clica em um botão na página ou algo do gênero);
2 – O controlador recebe o evento e coordena como as coisas vão acontecer do lado do servidor, ou seja, passa o fluxo para os objetos necessários para realizar a regra de negócio;
3 – O modelo é utilizado para representar o domínio das informações do negócio então ele é chamado pelo controlador para a realizar a regra de negócio que deve ser realizada. O modelo pode ainda ser usado para enviar os dados para a visão (camada onde o usuário as coisas acontecerem, uma página web por exemplo);
4 – Depois que o controlador realiza a regra de negócio utilizando o modelo ele renderiza a nova visão e a envia para o usuário. Criar uma aplicação utilizando o padrão MVC nada mais é do que separar a lógica em “pacotes” (um que representa a interface com o usuário – visão, um que conterá o domínio da aplicação – modelo, e um que terá as classes responsáveis por controlar o fluxo da aplicação – controlador) e fazer com que as informações sigam um caminho certo.

Após essa pequena introdução teórica vamos ver como o Spring implementa esse padrão:

Figura 1 – Fluxo de informações no Spring Web MVC

1 – A requisição do usuário sempre chega em um único servlet, o Dispatcher Servlet. Esse é um padrão muito comum em frameworks MVC chamado front controller (controlador frontal) onde um único servlet é responsável por receber a requisição, delegar o processamento para os outros componentes da aplicação e devolver uma resposta para o usuário;
2 – Uma vez que o Dispatcher Servlet tem a requisição ele precisa descobrir para qual controller essa requisição será enviada. Para isso ele pede ajuda para o Handler Mapping. Baseado na URL da requisição ele indica qual é o controlador a ser invocado;
3 – A requisição é então enviada para o controlador que cuidará dos dados contidos nela. Uma vez que o controlador recebe a requisição ele processa os dados que ela trouxe e executa alguma regra do negócio da aplicação (faz uma venda, adiciona um novo produto no estoque, aluga um dvd, abre uma os, …);
4 – Frequentemente a lógica processada pelo controlador resulta em alguma informação que deve ser levada de volta para o usuário (o model). Somente enviar a informação de volta não é o suficiente, ela deve ser formatada de um jeito que o usuário a entenda, para isso ela deve ser enviada para a view. Para isso o model e alguns dados da view são encapsulados em um objeto ModelAndView e retornados ao Dispatcher;
5 – Como o controlador não fica preso a apenas uma view ele envia uma dica no objeto ModelAndView para o Dispatcher saber para qual view deve enviar os dados. O Dispatcher repassa essa dica para o ViewResolver que devolve qual view deve ser chamada;
6 – Por fim o Dispatcher envia as informações (model) para a view (página JSP) que acabou de descobrir. A página renderiza as informações recebidas e é devolvida ao usuário.

Simples de entender, não!? :D

Vamos por a mão na massa então! A IDE será o NetBeans 6.9.1. Crie uma nova Aplicação Web (Java Web > Aplicação Web) e no último passo do assistente selecione o framework Spring Web MVC (Figura 2) e finalize.

Figura 2 – Incluir o framework Spring Web MVC no projeto

O NetBeans já cria alguns arquivos para nós (legal ele né). Vamos dar uma analisada nos códigos que ele gerou.

Figura 3 – Arquivos gerados pelo NetBeans

O arquivo de configuração do Dispatcher Servlet é o dispatcher-servlet.xml, nele são declarados todos os beans que o servlet precisa para conhecer os controladores e os outros componentes que o auxiliam no decorrer da aplicação.

Repare que o arquivo redirect.jsp é o único arquivo que fica fora do diretório WEB-INF. Isso é feito porque uma boa prática é que todas as views fiquem dentro do diretório WEB-INF para que só possam ser acessadas a partir do Dispatcher. O redirect.jsp apenas envia qualquer requisição feita na raiz da aplicação para a página index.jsp que está dentro do WEB-INF.

Para poder fazer esse redirecionamento é necessário que o fluxo explicado logo antes (6 passos) seja seguido. Para tal o NetBeans criou os seguintes beans no arquivo de configuração do Dispatcher:

<bean name="indexController"
	class="org.springframework.web.servlet.mvc.ParameterizableViewController"
	p:viewName="index" />

O controlador que será responsável por uma requisição à página index.jsp. Como esse controlador não vai fazer nenhum processamento dos dados da requisição foi criado um controlador da classe ParameterizableViewController que o Spring Web MVC já tem implementado. O que ele faz é apenas repassar um ModelAndView informando o nome da view (informado no parâmetro viewName) para o Dispatcher.

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
		<props>
			<prop key="index.htm">indexController</prop>
		</props>
	</property>
</bean>

Quando é utilizado o ParameterizableViewController é necessário mapear quais URLs serão direcionadas para aquele controlador, por isso foi criado o bean urlMapping da classe SimpleUrlHandlerMapping que apenas linka a url “index.htm” ao controlador indexController.

<bean id="viewResolver"
	class="org.springframework.web.servlet.view.InternalResourceViewResolver"
	p:prefix="/WEB-INF/jsp/"
	p:suffix=".jsp" />

viewResolver é quem vai ajudar o Dispatcher a saber para qual view encaminhar o ModelAndView que o controlador retornar (passo 5 do fluxo ali em cima). Como no ModelAndView só vai ter o nome lógico da view (no caso da index vai ter apenas a String index que foi definida no controlador indexController), o que o viewResolver faz é criar o caminho da view utilizando o prefixo e sufixo informados na declaração do bean. Para descobrir o caminho da view index ele vai fazer:

“WEB-INF/jsp/” + “index” + “.jsp” => resultando em “WEB-INF/jsp/index.jsp”

E por fim o NetBeans criou um bean sem id:

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

Esse bean é o cara que vai analisar todas as outras URLs e mapeá-las para os nossos controladores. Isso vai ficar mais claro daqui a pouquinho quando fizermos o nosso controlador.

Bom, vamos a nossa aplicação então, vou utilizar a idéia de uma aplicação que tem no livro Use a Cabeça: Servlets & JSP: uma aplicação que dá conselhos sobre cerveja! O usuário diz o tipo da cerveja que ele quer e nossa aplicação retorna alguns conselhos sobre quais cervejas ele poderia tomar. :D

Vamos aproveitar a estrutura que o NetBeans já criou e apenas alterar a página index.jsp para ser o nosso formulário onde o usuário vai indicar qual tipo de cerveja ele quer tomar:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Cervejeiro Online</title>
    </head>
 
    <body>
        <h3>Eu sou um cervejeiro profissional!</h3>
        <form action="sugestor.htm" method="post">
        <p>
            Qual tipo de cerveja você prefere?
            <select name="tipo">
                <option value="pilsen">Pilsen</option>
                <option value="chop">Chop</option>
                <option value="malzbier">Malzbier</option>
            </select>
        </p>
        <p><input type="submit" value="Quer saber minha sugestão?" /></p>
        </form>
    </body>
</html>

Quando o usuário pedir o conselho da nossa aplicação sobre cervejas do tipo que ele selecionou os dados serão enviados via POST para a URL sugestor.htm.

Lembra daquele bean que o NetBeans criou sem id (ControllerClassNameHandlerMapping)? Então, ele vai entrar em ação agora! Ele vai ser chamado pelo Dispatcher para identificar qual controlador vai receber a requisição.

O processo que esse bean utiliza para identificar qual controlador vai ser chamado é apenas pegar a String existente na URL até “.htm” e concatenar com “Controller”. Nesse caso o controlador que vai ser chamado é o SugestorController. Caso nosso formulário enviasse os dados para cervejeiro.htm o controlador que o Dispatcher tentaria invocar seria o CervejeiroController. Facinho não? ^^

Sendo assim vamos construir o nosso controlador:

public class SugestorController extends AbstractController {
 
    private SugestorService sugestor;
 
    public void setSugestor(SugestorService sugestor) {
        this.sugestor = sugestor;
    }
 
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
 
        String tipo = request.getParameter("tipo");
 
        List<cerveja> sugestoes = sugestor.sugerir(tipo);
 
        ModelAndView modelAndView = new ModelAndView("sugestao", "cervejas", sugestoes);
 
        return modelAndView;
    }
}

Uma coisa que não podemos esquecer: para o Spring entender que essa nossa classe é um controlador ela deve herdar de AbstractController ou qualquer outra classe que represente um controlador para o Spring Web MVC (pode ver mais exemplos aqui na referência oficial).

Uma boa prática no desenvolvimento de aplicativos MVC é nunca deixar toda a lógica dentro do controlador, usar ele apenas para delegar funções para outros componentes para que esses sim executem a lógica.

Para isso servem os famosos Services! Nosso controlador recebe um SugestorService (injetado pelo Spring) e delega para ele a função de sugerir as cervejas para o usuário.

Depois que o SugestorService retorna uma lista de cervejas (já já veremos a classe Cerveja que faz parte do model da nossa aplicação) para o controlador é hora de montar o ModelAndView que deve ser retornado para o Dispatcher.

Nosso ModelAndView foi instanciado com 3 parâmetros sendo o primeiro o nome da view (aquela dica que o bean viewResolver vai usar para montar o nome do arquivo lembra?), o segundo é o nome que a lista de cervejas terá na página JSP e o terceiro é a lista propriamente dita, quer dizer, quando formos mostrar as cervejas na página WEB-INF/jsp/sugestao.jsp (eu sei que você lembra como o viewResolver acha o arquivo da view, só escrevi ele ali pra você não esquecer mesmo.. hehehe) a lista terá o nome cervejas.

Figura 4 – Fluxo de componentes que são executados para uma requisição (sem contar com a view)

Com isso nosso controlador ficou muito bem projetado, recebe uma requisição, chama um Service para realizar a lógica de negócio da aplicação e devolve um ModelAndView para o Dispatcher. Exatamente o que se espera de um controlador: que ele controle os componentes necessários para a regra de negócio ser executada.

Vamos ver como o SugestorService funciona. Apenas um pequeno detalhe: SugestorService é uma interface que apenas diz que o Service que a implementar deve sugerir uma lista de cervejas a partir do tipo (é bom utilizar as interfaces! se você não tem esse costume passe a ter! Lendo os primeiros posts dessa série você vai entender porquê):

public interface SugestorService {
    List<cerveja> sugerir(String tipo);
}

Agora vejamos como esse Service funciona mesmo através do SugestorServiceImpl:

public class SugestorServiceImpl implements SugestorService {
 
    public List<cerveja> sugerir(String tipo) {
        List<cerveja> cervejasSugeridas = new ArrayList<cerveja>();
 
        if (tipo.equals("pilsen")){
            cervejasSugeridas.add(criarCerveja("Skol", 600));
            cervejasSugeridas.add(criarCerveja("Antartica", 600));
        } else if (tipo.equals("chop")){
            cervejasSugeridas.add(criarCerveja("Brahma", 600));
        } else if (tipo.equals("malzbier")){
            cervejasSugeridas.add(criarCerveja("Brahma Malzbier", 355));
            cervejasSugeridas.add(criarCerveja("Caracu", 355));
        }
 
        return cervejasSugeridas;
    }
 
    private Cerveja criarCerveja(String nome, Integer quantidade) {
        Cerveja cerveja = new Cerveja();
        cerveja.setNome(nome);
        cerveja.setQuantidade(quantidade);
        return cerveja;
    }
}

Apenas verificamos qual o tipo que o usuário selecionou e montamos uma lista de cervejas a partir dele (desculpe se não gosta das que coloquei, juro que tentei ser bem simples e eclético).

A classe que representa nosso modelo é um simples POJO com o nome e a quantidade (em mls) da cerveja:

public class Cerveja {
    private String nome;
    private Integer quantidade;
 
    public String getNome() {
        return nome;
    }
 
    public void setNome(String nome) {
        this.nome = nome;
    }
 
    public Integer getQuantidade() {
        return quantidade;
    }
 
    public void setQuantidade(Integer quantidade) {
        this.quantidade = quantidade;
    }
}

Bom, com toda a regra de negócio implementada vamos para a página que vai mostrar as cervejas sugeridas pela nossa aplicação (sugestao.jsp):

<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Minha Sugestão</title>
    </head>
    <body>
        <h3>Eu acho que você deveria experimentar:</h3>
        <p>
            <c:forEach items="${cervejas}" var="cerveja">
                <li>
                    <c:out value="${cerveja.nome}"/> -
                    <c:out value="${cerveja.quantidade}"/>ml
                </li>
            </c:forEach>
        </p>
        <p>
            <a href="index.htm">Voltar</a>
        </p>
	</body>
</html>

Apenas percorremos a lista de cervejas exibindo-as na tela (lembrando que o nome da lista é cervejas pois foi esse o nome que foi passado no ModelAndView lá no Controller).

Para fazermos nossa aplicação rodar precisamos apenas declarar o SugestorController e o SugestorServiceImpl no arquivo de configuração do Dispatcher Servlet:

<bean id="sugestorService" class="br.javasimples.cervejeiro.service.impl.SugestorServiceImpl" />
<bean name="sugestorController"
	class="br.javasimples.cervejeiro.controller.SugestorController">
	<property name="sugestor" ref="sugestorService" />
</bean>

Prontinho!! Simples códigos não? Só com essas pequenas classes nós construímos um Sugestor de Cervejas MVC!!! :D

Figura 5 – Escolhendo um tipo e pedindo a opinião da aplicação

Figura 6 – Cerveja sugerida pela aplicação

Espero que com esse pequeno tutorial e essa pequena aplicação você tenha entendido (pelo menos um pouco) o padrão MVC e como ele torna nossa aplicação muito bem estruturada. Dê uma olhada como os pacotes do meu projeto ficaram bem separados:

Figura 7 – Hierarquia de pastas e pacotes da aplicação

Caso queira pegar o projeto fique a vontade para baixá-lo por aqui.

Apenas um último conselho antes de encerrarmos este post: o que foi apresentado aqui foi a estrutura mais básica possível do Spring Web MVC, apenas com o que vimos você consegue fazer uma aplicação de pequeno porte porém eu aconselho fortemente você procurar aprender mais sobre o framework antes de utilizá-lo no desenvolvimento do seu projeto de graduação (hehehe).

O Spring Web MVC contém muitos (muitos mesmo!) outros beans que facilitam muito a vida do desenvolvedor! Vale mesmo a pena dar uma procurada por fontes mais avançadas. Um livro bem didático é o Spring in Action 2 (Craig Walls, 2008) e a referência é incrível!

Até a próxima!

[]s,
Saab.

10 Responses so far

Muito bom o seus posts!!!
Como faz para conectar com o BD no WEB?

Dê uma olhadinha nos posts “Spring Framework Parte 3″ e “Spring Framework Parte 4″ que falam só sobre isso.

=D

Olá, obrigado pelos seus tutoriais! Tenho uma pergunta, “ModelAndView modelAndView = new ModelAndView(“sugestao”, “cervejas”, sugestoes);” não tem como fazer por injeção de dependência?

Muito bom artigo! Obrigado :)

Boa tarde Saab,
seu tutorial me ajudou muito, e acabei escrevendo um tutorial complementar, citando seu site como base de estudo, espero que não tenha problema de ter feito isso, postei no GUJ, se puder dar uma olhada, qualquer coisa só entrar em contato:
http://www.guj.com.br/java/259976-apostila-tutorial-java-com-jsf–20-facelts-hibernate-jpa–spring-30-e-primefaces

Opa Diego,
Que fera rapaz!
Parabéns pelo artigo, dei uma lida por cima e em hora mais oportuna eu vou ler ele inteiro (quero aprender Prime Faces hehehe).
Obrigado por compartilhar com a galera.
[]s.

Fala aew Saab, teria muita diferença fazer esse tutorial no eclipse? Ja que ele não cria esses arquivos xml automaticamente?

Fala Vander,
Não tem diferença nenhuma.
Você pode baixar o fonte do projeto e dar uma olhada no cabeçalho dos XMLs que ele gera e criar ai no eclipse na mão mesmo.
Não dá nenhum trabalho a mais não.

Opa.. Muito bom seu artigo Saab! … Deu pra aprender muita coisa aqui… Agora estou lendo o do Diego sobre primeFaces … Obrigado aos 2 pelos materiais!

Cara simplesmente fantástico!!!!! Muito bom…. quando teremos as partes 6, 7, 8, 9……?!?!?!?!?

Parabéns!!!!

Leave a comment