Passo a passo para desenvolver uma aplicação usando o Spring Framework MVC – Capitulo 2 – Desenvolvendo e configurando as Visões e o Controlador

Nesse artigo, segue a Parte 2 do passo a passo para desenvolver uma aplicação web do zero usando o framework Spring. Na Parte 1 nós configuramos o ambiente de desenvolvimento e uma aplicação básica que iremos agora começar o desenvolvimento.

Isso é o que implementamos até agota:

  1. Uma página inicial, index.jsp, a página de boas vindas de nossa aplicação. Ela é usada para testar se nossa configuração esrtá correta. Nós a mudaremos mais tarde para que forneça um link para nossa aplicação.
  2. Um DispatcherServlet (controlador frontal) com um arquivo de configuração ‘springapp-servlet.xml’ correspondente.
  3. Um controlador de página, HelloController, com funcionalidade limitada – somente retorna um ModelAndView. Nesse memento temos um modelo vazio que irá ser preenchido mais tarde.
  4. Uma unidade de teste de classe para o controlador de páginas, HelloControllerTests, para verificar se o nome da visão é o esperado.
  5. Uma visão, hello.jsp, que novamente é extremamente básica. A boa notícia é que toda a configuração funciona e estamos prontos para adicionar mais funcionalidades.

2.1. Configurar o JSTL e adicionar o arquivo de cabeçalho JSP

Nós estaremos usando a Biblioteca de Tag Padrão do JSP (JSTL), então vamos começar copiando os arquivos JSP necessários para o nosso diretório WEB-INF/lib. Copie jstl.jar de spring-framework-X.X/lib/j2ee e standart.jar de spring-frameworl-X.X/lib/jakarta-taglibs.

Criaremos um arquivo de cabeçalho que será incluido em toda página JSP que iremos escrever. Assim garantimos que as mesmas definições serão incluidas em todos os nossos JSPs simplesmente incluido o arquivo de cabeçalho. Nós também iremos colocat todos os JSPs em um diretório chamado jsp dentro do diretório WEB-INF. Isso garantirá que as visões possam ser acessadas apenas via controlador já que não será possível acessar essas páginas diretamente via URL. Essa estratégia poderia não funcionar em alguns servidores de aplicação e se esse for o seu caso com o servidor que estiver usando, mova o diretório jsp um nível acima. Você deveria então usar springapp/war/jsp como diretório ao invés de springapp/war/WEB-INF/jsp em todo o código que se segue.

Primeiro criamos o arquivo de cabeçalho para inclusão em todos os JSPs que criaremos.

'springapp/war/WEB-INF/jsp/include.jsp':

<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

Agora vamos atualizar o index.jsp para usar esse arquivo e já que estamos usando JSTL, podemos usar a tag <c:redirect/> para redirecionar para nosso Controlador. Isso significa que todas as requisições por index.jsp irão passar por nosso frameworl. Só apague o conteúdo atual de index.jsp e subtitua pelo seguinte:

'springapp/war/index.jsp':

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
<c:redirect url="/hello.htm"/>

Mova o arquivo hello.jsp para WEB-INF/jsp. Adicione a mesma diretiva include que adicionamos a index.jsp. Nós também adicionaremos uma data e hora atual como saída para ser recuperadado modelo e passado para a visão que será desenhada usando a tag JSTL <c:out/>.

'springapp/war/WEB-INF/jsp/hello.jsp':

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
  <head><title>Hello :: Spring Application</title></head>
  <body>
    <h1>Hello - Spring Application</h1>
    <p>Greetings, it is now <c:out value="${now}"/></p>
  </body>
</html>

2.2. Aperfeiçoando o Controlador

Antes de atualizar a localização do JSP em nosso Controlador, vamos atualizar nossa unidade de teste primeiro. Sabemos que precisamos atualizar a referência do recursos da visão com a nova localização web-inf/jsp/hello.jsp. Nós também sabemos que deve existir um objeto no modelo mapeado par a a chave “now”.

'springapp/tests/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);
    }
}

Em seguida, executamos o alvo ‘tests’ do Ant e nosso teste deve falhar.

$ 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:<[WEB-INF/jsp/]hello.jsp> but was:<[]hello.jsp>
    [junit] junit.framework.ComparisonFailure: expected:<[WEB-INF/jsp/]hello.jsp> but was:<[]hello.jsp>
    [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 nós vamos atualizar HelloController pela configuração da referência do recurso da visão para sua nova localização WEB-INF/jsp/hello.jsp assim como configurar a chave/valor para a data e hora atual no modelo com o identificador: “now” e o valor: “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);
    }
}

Re-edecute os testes e agora deve passar sem erros.

Lembre que o Controlador já foi configurado no arquivo springapp-servlet.xml, assim estamos prontos para tenstar nossos aperfeiçoamentos depois de compilar e carregar esse novo código. Quando nós acessamos http://localhost:8080/springapp/ num navegador, deveria puxar o arquivo index.jsp que seria redirecionado para hello.htm e é manipulado pelo DispatchServlet, que delega nossa requisição para o controlador que coloca a data e a hora no modelo e então disponibiliza para a visão hello.jsp.

2.3. Desacoplando a visão do controlador

Nesse momento, o controlador especifica o caminho completo da visão, que cria uma dependência não necessária entre o controlador e a visão. Idealmente, nós gostariamos de mapear a visão usando um nome lógico, permitindo que nós troquemos a visão sem ter que alterar o controlador. Você pode configurar esse mapeamento em um arquivo de propriedades se quiser usar um ResourceBundleViewResolver e uma classe SimpleUrlHandlerMapping. Para o mapeamento básico de uma visão em um local, simplesmente configure um prefixo e um sufixo em InternalResourceViewResolver. Essa segunda abordagem é a que iremos implementar agora, assim modificamos o arquivo springapp-servlet.xml e declare uma entrada viewResolver. Pela escolha  do JstlView, ativaremos o uso do JSTL em combinação com o recurso de mensagens assim como nos fornece suporte para internacionalização.

'springapp/war/WEB-INF/springapp-servlet.xml':

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!-- the application context definition for the springapp DispatcherServlet -->
    <bean name="/hello.htm"/>
    <bean id="viewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

Atualizamos o nome da visão no controlador de teste HelloControllerTests para hello e re-execute o teste. Nesse momento, esse teste apresentará um erro.

'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);
    }
}

Agora removemos o prefixo e o sufixo do nome da visão no controlador, deixando o controlador referenciar a visão pelo seu nome lógico “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);
    }
}

Re-execute o teste e agora tudo passará sem erro.

Vamos compilar e carregar a aplicação e verifique se aplicação funciona.

2.4. Sumário

Vamos dar uma olhada no que nós criamos nessa Parte 2.

  1. Um arquivo de cabeçalho include.jsp, o JSP que contém as diretivas da taglib para as bibliotecas de tag que usaremos em nosso JSP.

Abaixo estão o artefatos existentes que mudamos na Parte 2.

  1. O HelloControllerTests foi atualizado repetidamente  pro termos feito o controlador referênciar o nome lógico ao invés do nome e localização fo arquivo.
  2. O controlador de página, HelloController, agora faz referência a visão pelo nome lógico da visão passando pelo uso do InternalResourceViewResolver definido em springapp-servlet.xml.

Abaixo segue a tela de como a estrutura de diretório de nosso projeto ficou depois das modificações acima.