Spring MVC passo-a-passo - Parte 2
Autor: Evaldo Wolkers | Publicado: 2012-03-19T22:14:00-07:00

Capítulo 2. Desenvolver e configurar os modos de exibição (View) e o controlador (Controller).

Esta é a segunda parte do tutorial sobre como desenvolver uma aplicação web a partir do zero usando Spring Framework.&nbsp;Na parte 1 configuramos o ambiente e criamos uma aplicação básica.

Veja o que implementamos até o momento:


·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Página de introdução,&nbsp;'index.jsp', A página de boas-vindas da aplicação. Ela foi utilizada para testar se nossa configuração está correta. Vamos mudar isto mais tarde para realmente fornecer um link para nossa aplicação.

·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;O&nbsp;DispatcherServlet&nbsp;(front controller) com o correspondente arquivo de configuração&nbsp;'springapp-servlet.xml'.

·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Um controlador de página,&nbsp;HelloController, com funcionalidade limitada – ele somente retorna um&nbsp;ModelAndView. Atualmente temos um modelo vazio, iremos fornecer um modelo completo mais tarde.

·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A classe de teste unitário do controlador de página,&nbsp;HelloControllerTests, para verificar se o nome da view é o que esperamos.

·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Uma view,&nbsp;'hello.jsp', Que é extremamente básica. A boa notícia é que fizemos todas configurações e agora estamos prontos para adionar mais funcionalidades.
















2.1.&nbsp;Configure JSTL e adicione o arquivo de cabeçalho JSP





Utilizaremos o JSP Standard Tag Library (JSTL), então vamos começar copiando os arquivos JSTL que precisamos para nossa pasta "WEB-INF/lib". Copie o arquivo jstl.jar da pasta 'spring-framework-2.5/lib/j2ee' e o arquivo standard.jar da pasta 'spring-framework-2.5/lib/jakarta-taglibs' para a pasta 'springapp/war/WEB-INF/lib'.





Vamos criar um arquivo de 'header' que será incluído em cada página JSP que vamos escrever. Assim garantimos que as mesmas definições estão incluídas em todos nossos JSPs. Vamos colocar todos JSPs em uma pasta chamada "jsp" dentro de 'WEB-INF'. Isso irá garantir que serão acessados apenas atravé do controller, uma vez que não será possível acessar estas páginas diretamente através de uma URL. Caso não funcione em seu servidor de aplicação, mova a pasta um nível acima, ficando em 'springapp/war/jsp' (se este for o teu caso, o caminho a ser utilizado em todos os exemplos é este).





Primeiro vamos criar o arquivo de header que será incluído em todos nossos JSPs.


'springapp/war/WEB-INF/jsp/include.jsp':
&lt;%@ page session="false"%&gt;
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %&gt;
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;

Agora atualize o&nbsp;'index.jsp'&nbsp;para utilizar este arquivo e já que estamos usando JSTL, você pode usar a tag&nbsp;&lt;c:redirect/&gt;&nbsp;para direcionar para o&nbsp;Controller. Isto significa que todos pedidos para 'index.jsp' irão passar pelo framework da aplicação. Substitua o conteúdo do 'index.jsp' por este:

'springapp/war/index.jsp':




&lt;%@ include file="/WEB-INF/jsp/include.jsp" %&gt;

&lt;%-- Redirected because we can't set the welcome page to a virtual URL. --%&gt;
&lt;c:redirect url="/hello.htm"/&gt;





Mova o arquivo&nbsp;'hello.jsp'&nbsp;para a pasta&nbsp;'WEB-INF/jsp'. Adicione a diretiva de inclusão no arquivo 'index.jsp'&nbsp;para&nbsp;'hello.jsp'. Também adicionaremos a data e hora como saída recuperada do modelo e passada à view processado usando a tag JSTL &lt;c:out/&gt;.



'springapp/war/WEB-INF/jsp/hello.jsp':
&lt;%@ include file="/WEB-INF/jsp/include.jsp" %&gt;

&lt;html&gt;
  &lt;head&gt;&lt;title&gt;Hello :: Spring Application&lt;/title&gt;&lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello - Spring Application&lt;/h1&gt;
    &lt;p&gt;Greetings, it is now &lt;c:out value="${now}"/&gt;&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;















2.2. Melhorando o controller






Antes de atualizar a localização do JSP no controller, atualize sua classe de teste unitário antes. Sabemos que precisamos atualizar os recursos das views referente a nova localização&nbsp;'WEB-INF/jsp/hello.jsp'. Sabemos também que deve haver um objeto no modelo mapeado para a chave&nbsp;"now".



'springapp/test/springapp/web/HelloControllerTests.java':
package springapp.web;

import org.springframework.web.servlet.ModelAndView;

import springapp.web.HelloController;

import junit.framework.TestCase;

public class HelloControllerTests extends TestCase {

    public void testHandleRequestView() throws Exception{
        HelloController controller = new HelloController();
        ModelAndView modelAndView = controller.handleRequest(null, null);
        assertEquals("WEB-INF/jsp/hello.jsp", modelAndView.getViewName());
        assertNotNull(modelAndView.getModel());
        String nowValue = (String) modelAndView.getModel().get("now");
        assertNotNull(nowValue);
    }
}

Agora vamos executar o teste Ant&nbsp;'tests'.
$ ant tests
Buildfile: build.xml

build:

buildtests:
    [javac] Compiling 1 source file to /home/trisberg/workspace/springapp/war/WEB-INF/classes

tests:
    [junit] Running springapp.web.HelloControllerTests
    [junit] Testsuite: springapp.web.HelloControllerTests
    [junit] Oct 31, 2007 1:27:10 PM springapp.web.HelloController handleRequest
    [junit] INFO: Returning hello view
    [junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.046 sec
    [junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.046 sec
    [junit] 
    [junit] ------------- Standard Error -----------------
    [junit] Oct 31, 2007 1:27:10 PM springapp.web.HelloController handleRequest
    [junit] INFO: Returning hello view
    [junit] ------------- ---------------- ---------------
    [junit] Testcase: testHandleRequestView(springapp.web.HelloControllerTests):        FAILED
    [junit] expected:&lt;[WEB-INF/jsp/]hello.jsp&gt; but was:&lt;[]hello.jsp&gt;
    [junit] junit.framework.ComparisonFailure: expected:&lt;[WEB-INF/jsp/]hello.jsp&gt; but was:&lt;[]hello.jsp&gt;
    [junit]     at springapp.web.HelloControllerTests.testHandleRequestView(HelloControllerTests.java:14)
    [junit] 
    [junit] 
    [junit] Test springapp.web.HelloControllerTests FAILED

BUILD FAILED
/home/trisberg/workspace/springapp/build.xml:101: tests.failed=true
            ***********************************************************
            ***********************************************************
            ****  One or more tests failed!  Check the output ...  ****
            ***********************************************************
            ***********************************************************

Total time: 2 seconds

Agora vamos atualizar&nbsp;HelloController&nbsp;configurando o recurso da view referente à nova localização&nbsp;'WEB-INF/jsp/hello.jsp'&nbsp;bem como definir o part key/value para a data/hora corrente no modelo com a chave de identificação:&nbsp;"now"&nbsp;e o valor:&nbsp;'now'.

'springapp/src/springapp/web/HelloController.java':
package springapp.web;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.Date;

public class HelloController implements Controller {

    protected final Log logger = LogFactory.getLog(getClass());

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String now = (new Date()).toString();
        logger.info("Returning hello view with " + now);

        return new ModelAndView("WEB-INF/jsp/hello.jsp", "now", now);
    }

}

Execute o ant&nbsp;'tests'&nbsp;para verificar se está tudo ok.


Lembre-se que o&nbsp;Controller&nbsp;já foi configurado no arquivo&nbsp;'springapp-servlet.xml', então estamos prontos para experimentar os nossos aprimoramentos depois de contruimos e implantarmos esse novo código. Quando digitar&nbsp;http://localhost:8080/springapp/&nbsp;no browser, ele deve subir o arquivo de boas-vindas&nbsp;'index.jsp', que deve redirecionar para&nbsp;'hello.htm'&nbsp;e é tratado pelo&nbsp;DispatcherServlet, que envia o pedido para o controller que adiciona na página data/hora e, em seguida, torna o modelo disponível para a view&nbsp;'hello.jsp'.









Aplicação atualizada














2.3.&nbsp;Dissociar a view do controller







Neste momento, o controller especifica o caminho completo da view, o que cria uma dependência desnecessária entre o controller e a view. O ideal é mapear a view usando um nome lógico, o que nos permite mudar a view sem ter que alterar o controller. Você pode configurar este mapeamento em um arquivo de propriedades se quiser usar&nbsp;ResourceBundleViewResolver&nbsp;e aSimpleUrlHandlerMapping&nbsp;class. Para um mapeamento básico da view para uma localização, basta definir um prefixo e um sufixo em&nbsp;InternalResourceViewResolver. Esta segunda abordagem é a que vamos implementar agora, para isso modifique o&nbsp;'springapp-servlet.xml'&nbsp;declarando a entrada&nbsp;'viewResolver'. Ao escolher&nbsp;JstlView, podemos utilizar JSTL em combinação com pacotes de recursos de mensagens, nos fornecendo também apoio à internacionalizacão.


'springapp/war/WEB-INF/springapp-servlet.xml':
&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"&gt;
    
    &lt;!-- the application context definition for the springapp DispatcherServlet --&gt;
    
    &lt;bean name="/hello.htm" class="springapp.web.HelloController"/&gt;
    
    &lt;bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt;
        &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"&gt;&lt;/property&gt;
        &lt;property name="prefix" value="/WEB-INF/jsp/"&gt;&lt;/property&gt;
        &lt;property name="suffix" value=".jsp"&gt;&lt;/property&gt;        
    &lt;/bean&gt;
            
&lt;/beans&gt;

Atualize o nome da view na classe de teste HelloControllerTests&nbsp;para&nbsp;'hello'&nbsp;e execute novamente para verificar a falha.

'springapp/test/springapp/web/HelloControllerTests.java':
package springapp.web;

import org.springframework.web.servlet.ModelAndView;

import springapp.web.HelloController;

import junit.framework.TestCase;

public class HelloControllerTests extends TestCase {

    public void testHandleRequestView() throws Exception{
        HelloController controller = new HelloController();
        ModelAndView modelAndView = controller.handleRequest(null, null);
        assertEquals("hello", modelAndView.getViewName());
        assertNotNull(modelAndView.getModel());
        String nowValue = (String) modelAndView.getModel().get("now");
        assertNotNull(nowValue);
    }
}

Em seguida, retire o prefixo e sufixo do nome da view do controller, deixando o controller fazer referência à view pelo seu nome&nbsp;"hello".

'springapp/src/springapp/web/HelloController.java':
package springapp.web;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.Date;

public class HelloController implements Controller {

    protected final Log logger = LogFactory.getLog(getClass());

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String now = (new Date()).toString();
        logger.info("Returning hello view with " + now);

        return new ModelAndView("hello", "now", now);
    }

}

Execute o teste novamente, agora deve passar.

Vamos compilar e realizar o deploy para ver se a aplicação está funcionando










2.4.&nbsp;Sumário






Vamos verificar rapidamente o que criamos na Parte 2.



O arquivo de cabeçalho&nbsp;'include.jsp', o JSP que contém as diretivas taglib para as bibliotecas de tags que estaremos usando em nossos JSPs.




Estes são os artefatos que mudaram na Parte 2.



O&nbsp;HelloControllerTests&nbsp;foi atualizado várias vezes à medida que fizemos o controller referenciar o nome lógico da view em vez de seu nome codificado e sua localização.


O controlador de página,&nbsp;HelloController, agora referencia &nbsp;a exibição pelo seu nome lógico através do uso do&nbsp;'InternalResourceViewResolver'&nbsp;definido em&nbsp;'springapp-servlet.xml'.




Segue abaixo uma captura de tela&nbsp;de como a estrutura do seu projeto deve estar após seguir os passos acima.





Estrutura de diretórios após finalizar a Parte 2.



Publicado em:&nbsp;http://static.springsource.org/docs/Spring-MVC-step-by-step/part2.html





Evaldo Wolkers.