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:
- Spring – Latest GA release: 3.1.2.RELEASE: http://www.springsource.org/download/community.
- Apache Commons (1.1.1): http://commons.apache.org/logging/download_logging.cgi
- CGLIB 2.2.3: http://sourceforge.net/projects/cglib/files/
- MySQL 5.5.28: http://www.mysql.com/downloads/
- JSTL API 1.2.1 e JSTL Implementation 1.2.1: http://jstl.java.net/download.html
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
.
package com.wp.mb.tutorial.model; /** * http://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.
package com.wp.mb.tutorial.dao; import com.wp.mb.tutorial.model.User; import java.util.List; /** * http://www.mballem.com/ */ public interface IUserDAO { void save(User user); ListfindAll(); }
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; /** * http://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 ListfindAll() { 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
.
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; /** * http://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 ListfindAll() { 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, url
, user
, pass
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.
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; /** * http://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.
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; /** * http://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.
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; /** * http://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() { Mapmodel = 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.
<?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.
<%@ 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/.
<%@ 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>
<%@ 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:
Acesse sua aplicação através da url: http://localhost:8080/tutorial-spring-mvc. Você terá a seguinte pagina aberta:
Clique em “Add User” para adicionar usuários no banco de dados, assim você verá a seguinte página:
Ao salvar um usuário você será redirecionado para a página de lista de usuários:
Saiba mais em: