Spring MVC 100% livre de XML

Um dos grandes sucessos do Spring Framework foi a possibilidade fornecida do uso do padrão MVC através do Spring MVC. Até a versão 2.5 do framework, toda essa configuração era realizada em arquivos do tipo XML. Quando a versão 3 foi lançada, os beans do chamado Controller, passaram a ser configurados com o uso de algumas anotações, entre elas a anotação @Controller. Em artigos anteriores, como Spring Framework 3 – 100% Livre de XML e Spring Framework 3 – Configurando o Serviço JavaMail, demonstrei como trabalhar com o Spring sem a necessidade do uso de configuração por XML. Desta vez, o foco será no Spring MVC sem o uso de XML. Para isso, vamos ver um exemplo simples de uma aplicação web que usará o padrão o MVC.

1. Preparando o projeto

Para o exemplo deste tutorial será necessário usar as bibliotecas do Spring Framework 3, entre outras, como as listadas abaixo:

2. Iniciando o projeto

Crie um projeto web na sua IDE de preferência, adicione as bibliotecas citadas e em seguida crie a classe de entidade User.

Listagem 1. Classe User.
package com.wp.mb.tutorial.model;

/**
 * https://www.mballem.com/
 */
public class User implements Serializable {
    private long id;
    private String name;
    private String login;
    private String pass;

    //gere os métodos get/set e toString().
}

Vamos agora criar a interface IUserDAO – Listagem 2 – e classe UserDAO – Listagem 3 – usando como modo de persistencia a API JdbcTemplate do Spring Framework. Como o exemplo será simples, teremos apenas um método para inserir e outro para listar os dados persistidos. A classe UserDAO será um bean gerenciado pelo Spring atravé do uso da anotação @Repository. Injetamos o bean jdbcTemplate por meio do método construtor da classe, o qual recebe a anotação @Autowired responsavel por gerenciar a injenção do bean. Temos também um método chamado createTable() e este, será resposavel por criar a tabela USERS no banco de dados a partir da primeira execução da aplicação.

Listagem 2. Interface IUserDAO.
package com.wp.mb.tutorial.dao;

import com.wp.mb.tutorial.model.User;

import java.util.List;

/**
 * https://www.mballem.com/
 */
public interface IUserDAO {
    void save(User user);

    List findAll();
}

 

Listagem 3. Classe UserDAO.
package com.wp.mb.tutorial.dao;
import com.wp.mb.tutorial.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
* https://www.mballem.com/
*/
@Repository
public class UserDAO implements IUserDAO {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public UserDAO(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        createTable();
    }

    @Override
    public void save(User user) {
        String sql =
        "insert into USERS (name, login, pass) values (?,?,?)";

        jdbcTemplate.update(sql, user.getName(), user.getLogin(), user.getPass());
    }

    @Override
    public List findAll() {
        return jdbcTemplate.query("select * from Users", new UserMapper());
    }

    private static final class UserMapper implements RowMapper {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId((Long) rs.getObject("id"));
            user.setName(rs.getString("name"));
            user.setLogin(rs.getString("login"));
            user.setPass(rs.getString("pass"));
            return user;
        }
    }

    private void createTable() {
        String sql =
		"CREATE TABLE IF NOT EXISTS USERS ("+
		"  id bigint(10) NOT NULL AUTO_INCREMENT," +
		"  name varchar(25) NOT NULL," +
		"  login varchar(25) NOT NULL UNIQUE," +
		"  pass varchar(8) NOT NULL," +
		"  PRIMARY KEY (ID)" +
		");";

        jdbcTemplate.execute(sql);
    }
}

Vamos agora criar a classe UserService (Listagem 4). Em um projeto montado sobre padrões, o pacote service armazena as classes voltadas para as regras de negócios do sistema. No Spring usamos a anotação @Service para transformar uma classe de regra de negócio em um bean gerenciável pelo framework. A injeção de qualquer dependência fica por parte da anotação @Autowired, como já vimos anteriormente também na classe UserDAO, a qual será injetada em UserService para se ter acesso aos métodos de UserDAO.

Listagem 4. Classe UserService.
package com.wp.mb.tutorial.service;

import com.wp.mb.tutorial.dao.IUserDAO;
import com.wp.mb.tutorial.model.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * https://www.mballem.com/
 */
@Service
public class UserService {

    private IUserDAO dao;

    @Autowired
    public UserService(IUserDAO dao) {
        this.dao = dao;
    }

    public void save(User user) {
        dao.save(user);
    }

    public List findAll() {
        return dao.findAll();
    }
}

3. Configurando os Beans sem uso de arquivos XML

A partir de agora vamos construir cinco classes, onde teremos a classe BeanDataSource responsável pela conexão com o banco de dados e também por criar o bean jdbcTemplate.Teremos ainda a classe BeanDaoSource, responsável pelos beans do tipo DAO; a classe BeanServiceSource, responsável pelos beans do tipo Service; a classe BeanControllerSource, responsável pelo gerenciamento dos beans do tipo Controller; e por fim o BeanSpringWebSource, o qual terá a configuração do ViewResolver, e assim, substituirá a necessidade do arquivo spring-web.xml.

Na Listagem 5 temos a classe BeanDataSource. Nesta classe vamos configurar a conexão com o banco de dados e também criar o bean JdbcTemplate, o qual posteriormente será injetado em nosso DAO. Temos também quatro variáveis de instancia, urluserpass e driver, que representam os dados de conexão que estão armazenados no arquivo de propriedades, jdbc.properties (deve ser adicionado no classpath da aplicação):

jdbc.url = jdbc:mysql://localhost/agencia_db
jdbc.username = root
jdbc.password = root
jdbc.driver = com.mysql.jdbc.Driver

Sempre que você for criar um bean ele deverá ser anotado com a anotação @Bean. A classe anotada com @Configuration substitui a necessidade da criação de arquivos XML para a configuração dos beans. Usamos as anotações @Value para capturar os valores contidos no arquivo de propriedade e através do bean propriedades o Spring consegue ler este arquivo e carregar os valores nas variáveis anotadas. A anotação @ComponentScan mapeia para o Spring o pacote principal onde contém as classes anotadas como beans.

Listagem 5. Classe BeanDataSource.
package com.wp.mb.tutorial.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderSupport;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * https://www.mballem.com/
 */
@Configuration
@ComponentScan(basePackages = "com.wp.mb.tutorial")
public class BeanDataSource {

    private DataSource dataSource;

    @Value("#{propriedades['jdbc.url']}")
    private String url;
    @Value("#{propriedades['jdbc.username']}")
    private String user;
    @Value("#{propriedades['jdbc.password']}")
    private String pass;
    @Value("#{propriedades['jdbc.driver']}")
    private String driver;

    @Bean(name = "dataSource")
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setPassword(pass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        return this.dataSource = dataSource;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate jdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource());
        return jdbcTemplate;
    }

    @Bean(name = "transaction")
    public PlatformTransactionManager transactionManager() {
        if (dataSource == null) {
            dataSource();
        }

        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "propriedades")
    public PropertiesLoaderSupport jdbcProperties() {
        PropertiesFactoryBean props = new PropertiesFactoryBean();
        props.setLocation(new ClassPathResource("/jdbc.properties"));
        return props;
    }
}

Quando configuramos o Spring MVC por meio de arquivos XML, devemos ter um arquivo chamado spring-web.xml, o qual contém a configuração do que chamamos de resolver. O resolver é a forma como o Spring retorna o resultado dos controladores para as paginas HTML. Em substituição ao arquivo XML, vamos implementar o BeanSpringWebSource, conforme a Listagem 6. Usamos além da anotação @Configuration, a anotação @EnableWebMVC, para informar ao Spring que este será o bean web MVC. Também devemos estender a classe org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter, que implementa métodos uteis a parte web da aplicação.

Listagem 6. Classe BeanSpringWebSource.
package com.wp.mb.tutorial.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * https://www.mballem.com/
 */
@Configuration
@EnableWebMvc
public class BeanSpringWebSource extends WebMvcConfigurerAdapter {

    private static final String VIEW_RESOLVER_PREFIX = "/WEB-INF/view/";
    private static final String VIEW_RESOLVER_SUFFIX = ".jsp";

    @Bean(name = "jspViewResolver")
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();

        resolver.setPrefix(VIEW_RESOLVER_PREFIX);
        resolver.setSuffix(VIEW_RESOLVER_SUFFIX);
        resolver.setViewClass(JstlView.class);

        return resolver;
    }
}

Na Listagem 7 temos a classe UserController. A anotação @Controller, torna a classe um bean gerenciável pelo Spring. As anotações @RequestMapping tem como finalidade informar como o Spring deve acessar a classe e os métodos da classe. E novamente usamos a anotação @Autowired para injetar o bean usuarioService no controller.

Listagem 7. Classe UserController.
package com.wp.mb.tutorial.web.controller;

import com.wp.mb.tutorial.model.User;
import com.wp.mb.tutorial.service.UserService;

import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

/**
 * https://www.mballem.com/
 */
@Controller
@RequestMapping("/users")
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    //quando clicar no botao salvar, vem para este método
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public ModelAndView saveUser(@ModelAttribute("user") User user, BindingResult result) {
        userService.save(user);
        return new ModelAndView("redirect:/users.html");
    }

    //lista os users
    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView listUsers() {
        Map model = new HashMap();
        model.put("users", userService.findAll());

        return new ModelAndView("userList", model);
    }

    //a pagina abre aqui quando vem do index
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addArticle(@ModelAttribute("user") User user, BindingResult result) {
        return new ModelAndView("userAdd");
    }
}

Na Listagem 8 temos o arquivo web.xml, onde indicamos ao contexto servidor web, a classe que irá inicializar o container do Spring. Como estamos utilizando configuração 100% programática, informamos ao Spring que ele deve usar os parametros contexClass, para indicar que o contexto é por meio de classes e não XML, e a classe motor do Spring será a AnnotationConfigWebApplicationContext. Outro parametro informado será o contextConfigLocation, o qual indica em qual pacote estará a classe de configuração dos beans. As demais configurações são semelhantes as usadas quando se faz uso de configuração do Spring por XML.

Listagem 8. Arquivo web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <display-name>tutorial-spring-mvc</display-name>

    <welcome-file-list>
        <welcome-file>/index.jsp</welcome-file>
    </welcome-file-list>

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.wp.mb.tutorial.configuration</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>                 
                 org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

Veja na Listagem 9 a página índex.jsp. Esta página será a página inicial da aplicação. Nela teremos 2 links, onde um nos levará para uma página de cadastro de usuários e outro no levará para uma página que exibe os usuários cadastrados.

Listagem 9. Pagina index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title></title>
  </head>
  <body>
  <h1>Spring 3 MVC  application without XML configuration.</h1>
  <a href="users.html">List of Users</a>
  <a href="users/add.html">Add User</a>
  </body>
</html>

Na Listagem 10 temos a página userAdd.jsp e na Listagem 11 a página userList.jsp. Adicione as duas páginas dentro do diretório WEB-INF/view/.

Listagem 10. Página userAdd.jsp
<%@  taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>

<html>
<head><title>Add User</title></head>
<body>
<h1>Add User</h1>

<c:url var="viewUsersUrl" value="/users.html"/>
<a href="${viewUsersUrl}">Show All Users</a>
<br/>
<br/>
<c:url var="saveUserUrl" value="/users/save.html"/>
<form:form modelAttribute="user" method="POST" action="${saveUserUrl}">
    <form:label path="name">User name:</form:label>
    <form:input path="name"/>
    <br/>
    <form:label path="login">User login:</form:label>
    <form:input path="login"/>
    <br/>
    <form:label path="pass">User password:</form:label>
    <form:password path="pass"/>
    <br/>
    <input type="submit" value="Save User"/>
</form:form>
</body>
</html>
Listagem 11. Página userList.jsp
<%@  taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <title>All Users</title>
</head>
<body>
<h1>List Users</h1>

<a href="users/add.html">Add User</a>
<c:if test="${!empty users}">
    <table>
        <tr bgcolor=#f5f5dc>
            <th align="left" width="100px">User ID</th>
            <th align="left" width="150px">User Name</th>
            <th align="left" width="150px">User Login</th>
            <th align="left" width="100px">User Password</th>
        </tr>
        <c:forEach items="${users}" var="user" varStatus="id">
            <tr  bgcolor="#${id.count % 2 != 0 ? 'aaee88' : 'ffffff' }">
                <td align="left" width="100px"><c:out value="${user.id}"/></td>
                <td align="left" width="150px"><c:out value="${user.name}"/></td>
                <td align="left" width="150px"><c:out value="${user.login}"/></td>
                <td align="left" width="100px"><c:out value="${user.pass}"/></td>
            </tr>
        </c:forEach>
    </table>
</c:if>
</body>
</html>

Veja na figura 1 a seguir a estrutura do projeto:

Ilustração 1 - Estrutura do Projeto

Figura 1 – Estrutura do Projeto

Acesse sua aplicação através da url: http://localhost:8080/tutorial-spring-mvc. Você terá a seguinte pagina aberta:

Fígura 2 - Página índex

Fígura 2 – Página índex

Clique em “Add User” para adicionar usuários no banco de dados, assim você verá a seguinte página:

Ilustração 3 - Página add user

Figura 3 – Página add user

Ao salvar um usuário você será redirecionado para a página de lista de usuários:

Fígura 4 - Página list users

Fígura 4 – Página list users

Saiba mais em:

Maven Project - Githubdirect download

Ballem

Marcio Ballem é bacharel em Sistemas de Informação pelo Centro Universitário Franciscano em Santa Maria/RS. Tem experiência com desenvolvimento Delphi e Java em projetos para gestão pública e acadêmica. Possui certificação em Java, OCJP 6.

Você pode gostar...