Spring 3 MVC com Validator e Converter
Neste tutorial a abordagem será sobre o Spring MVC e sua capacidade de validar dados inseridos no formulário (view), como também a conversão de valores que vem do formulário em formato String, mas que devem ser recebidos como um objeto de outro tipo especifico. Para exemplificar um validator e também o processo de conversão de valores, teremos um pequeno projeto como exemplo.
1. Classes de Entidade
Como o projeto será simples, serão utilizadas apenas duas classes de entidades, a classe Profile que terá um atributo identificador e um atributo de descrição para o perfil. A classe User conterá além de um identificador, um atributo java.lang.String para o nome do usuário, um atributo java.util.Date para a data de nascimento, e por fim, um atributo do tipo Profile, para vincular o usuário a um perfil. O relacionamento será do tipo 1-N, onde um perfil poderá ser encontrado entre vários usuários enquanto um usuário terá apenas um único perfil.
package com.mballem.springconverter.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* http://www.mballem.com/
*/
@Entity
@Table(name = "PROFILES")
public class Profile implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "PROFILE_ID")
private Long id;
@Column(name = "PROFILE_DESC")
private String profile;
@OneToMany(cascade=CascadeType.REFRESH, fetch=FetchType.EAGER)
@JoinColumn(name="USER_ID")
private List user;
//getters and setters
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Profile profile = (Profile) o;
if (id != null ? !id.equals(profile.id) : profile.id != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
} package com.mballem.springconverter.entity; import javax.persistence.*; import java.io.Serializable; import java.util.Date; /** * http://www.mballem.com/ */ @Entity @Table(name = "USERS") public class User implements Serializable, Comparable{ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "USER_ID") private Long id; @Column(name = "USER_NAME") private String name; @Temporal(value = TemporalType.DATE) @Column(name = "USER_DATE_OF_BIRTH") private Date birthDate; @ManyToOne @JoinColumn(name="PROFILE", nullable=false, updatable=true) private Profile profile; //getters and setters @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (id != null ? !id.equals(user.id) : user.id != null) return false; return true; } @Override public int hashCode() { return id != null ? id.hashCode() : 0; } @Override //usado para ordenar por nome. public int compareTo(User user) { return this.name.compareTo(user.getName()); } }
2. Padrão DAO
O segundo passo será o desenvolvimento das classes de interação com o banco de dados. Para isso, vamos usar o padrão DAO e também a SessionFactory do Hibernate 4 que será injetada nos dao’s através do Spring e seu componente de inversão de controle e injeção de dependências. Nas Listagens 3 e 4 temos respectivamente a interface IProfileDao e a classe concreta ProfileDao, já nas Listagens 5 e 6, teremos a interface IUserDao e a classe concreta UserDao.
package com.mballem.springconverter.dao; import java.io.Serializable; import java.util.List; /** * http://www.mballem.com/ */ public interface IProfileDao{ Profile find(Long id); List findAll(); }
package com.mballem.springconverter.dao; import java.io.Serializable; import java.util.List; public interface IProfileDao{ Profile find(Long id); List findAll(); } package com.mballem.springconverter.dao; import com.mballem.springconverter.entity.Profile; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Repository @Transactional public class ProfileDao implements IProfileDao { @Autowired private SessionFactory sessionFactory; public Session getSession() { return sessionFactory.getCurrentSession(); } @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Profile find(Long id) { return (Profile) getSession().createCriteria(Profile.class) .add(Restrictions.idEq(id)).uniqueResult(); } @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List findAll() { return getSession().createCriteria(Profile.class).list(); } }
package com.mballem.springconverter.dao; import java.io.Serializable; import java.util.List; public interface IUserDao{ void saveOrUpdate(User entity); void delete(User entity); User find(Long id); List findAll(); }
package com.mballem.springconverter.dao; import com.mballem.springconverter.entity.User; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.List; @Repository @Transactional public class UserDao implements IUserDao{ @Autowired private SessionFactory sessionFactory; public Session getSession() { return sessionFactory.getCurrentSession(); } @Override public void saveOrUpdate(User entity) { getSession().saveOrUpdate(entity); } @Override public void delete(User entity) { getSession().delete(entity); } @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public User find(Long id) { return (User) getSession().createCriteria(User.class) .add(Restrictions.idEq(id)).uniqueResult(); } @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List findAll() { List users = getSession().createCriteria(User.class).list(); Collections.sort(users); //ordena a lista por name de a-z. return users; } }
Sobre as anotações do Spring, estamos usando a @Repository para marcar a classe como um bean do tipo repositório, usado comumente em classes de persistência. A anotação @Autowired serve como indicação de que a propriedade terá uma instancia de sua classe injetada pelo Spring sempre que seu uso for necessário. A anotação @Transactional define os requisitos de transação para a classe ou para métodos específicos.
3. Pacote web
Dentro do pacote web vamos adicionar três pacotes que serão, validator, controller e converter. O pacote validator conterá a classe de validação dos dados inseridos no formulário JSP, o pacote converter terá as classes conversoras de dados entre páginas JSP e entidades. O controller, conterá a classe UserController da aplicação.
A classe UserValidator, da Listagem 7, deve implementar a interface org.springframework.validation.Validator, assim, devemos programar dois métodos referentes a interface, o método supports() que valida se a classe a ser validada é realmente a classe que está acessando o validador e o método validate(). Neste último será onde vamos aplicar a validação propriamente dita. O método possui dois parâmetros, um Object que neste caso conterá o objeto da classe User e o Errors, o qual teremos acesso aos tipos de métodos de validação do Spring e também responsavel por repassar para a JSP, as mensagens de erro. As mensagens de erros estão armazenadas em um arquivo de propriedades chamado ValidationMessages.properties, o qual está descrito abaixo:
required.name = User name is required! required.profile = Select a profile, please! required.birthDate = Date of birth is required (dd/mm/yyyy).
package com.mballem.springconverter.web.validator;
import com.mballem.springconverter.entity.User;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import java.util.Date;
@Component
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> aClass) {
return User.class.equals(aClass);
}
@Override
public void validate(Object obj, Errors errors) {
// verifica se o campo name está fazio ou apenas com espaços em branco
ValidationUtils
.rejectIfEmptyOrWhitespace(errors, "name", "required.name");
User user = (User) obj;
// se o objeto profile for null o estoura o erro
if (user.getProfile() == null || user.getProfile().equals("0")) {
errors.rejectValue("profile", "required.profile");
}
// se o objeto birthDate for null estoura o erro
if (user.getBirthDate() == null) {
errors.rejectValue("birthDate", "required.birthDate");
}
}
}
Vamos agora conhecer as classes de conversão. Na Listagem 8 temos a classe de conversão chamada BirthdatePropertyEditor, responsável por converter o objeto String que retorna da JSP em um objeto java.util.Date e também mantém esse objeto em um formado especifico que será: dd/MM/yyyy. A classe de conversão, por padrão, contém como parte do seu nome as palavras PropertyEditor. Isto acontece porque essa classe herda métodos necessário para a conversão da super-classe PropertyEditorSupport. Os métodos herdados e os quais você deverá implementar são: setAsText() e getAsText(). O método setAsText() recebe como par mentro uma String contendo o conteúdo que foi digitado pelo usuário na página JSP. Já o método getAsText() retorna para a página JSP o objeto no formato String.
package com.mballem.springconverter.web.converter;
import org.apache.log4j.Logger;
import java.beans.PropertyEditorSupport;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
public class BirthdatePropertyEditor extends PropertyEditorSupport {
private static final Logger LOG =
Logger.getLogger(BirthdatePropertyEditor.class);
private final DateFormat dateFormat;
public BirthdatePropertyEditor(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
@Override
public void setAsText(String text) {
try {
Date date = (Date) dateFormat.parseObject(text);
setValue(date);
} catch (ParseException e) {
LOG.fatal("error setting date for String: " + text, e);
}
}
/**
* Format the Date as String, using the specified DateFormat.
*/
@Override
public String getAsText() {
Date value = (Date) getValue();
return (value != null ? this.dateFormat.format(value) : "");
}
}
Na Listagem 9 temos a classe ProfilePropertyEditor. Ela trabalha da seguinte forma. Na JSP temos um componente HTML do tipo combo, onde o usuário poderá escolher o a descrição do perfil entre varias opções, que neste caso serão apenas três: ADM, USER e GUEST. Porém, quando o usuário selecionar entre um deles na JSP, o retorno será o id referente a descrição. O objeto User no controller, espera por um objeto do tipo Profile e não por um id do tipo String. Então, precisamos converter esse id em um objeto Profile. Para isso, usamos o método de consulta find() que receber como parâmetro um id que será usado como critério na query. O retorno desta consulta será um objeto Profile o qual é devolvido para o objeto User pelo método setValue(). Neste caso não precisamos implementar o método getAsText() porque para a JSP não retornamos o objeto Profile e sim sua descrição que já é uma String.
package com.mballem.springconverter.web.converter;
import com.mballem.springconverter.dao.IProfileDao;
import com.mballem.springconverter.entity.Profile;
import java.beans.PropertyEditorSupport;
public class ProfilePropertyEditor extends PropertyEditorSupport {
private IProfileDao dao;
public ProfilePropertyEditor(IProfileDao dao) {
this.dao = dao;
}
@Override
public void setAsText(String text) {
//transforma a string com o id em um long
Long id = new Long(text);
//recupera no db o profile do id referido
Profile profile = dao.find(id);
//add o objeto profile, o qual faz parte do objeto user no controller
super.setValue(profile);
}
}
Na Listagem 10 temos a classe controller e primeiramente vamos dar atenção ao método initBinder(). Este método será o primeiro método a ser executado quando o controller for chamado. Através do parâmetro ServletRequestDataBinder, temos acesso ao método setValidator(), o qual recebe uma instancia da classe de validação, neste caso UserValidator. Vamos ter acesso também ao método registerCustomEditor(), o qual recebe o tipo de objeto que será convertido e uma instancia da classe conversora. A anotação @Controller da classe UserController é necessária para informar ao Spring que esta é uma classe do tipo controller. Já a anotação @RequestMapping é usada para a URL que irá acessar este controller. No caso a URL principal do nosso sistema será http://localhost:8080/spring-converter/.
Para acessar os métodos do UserController, devemos ter a url http://localhost:8080/spring-converter/users. Nos métodos das classes temos também a anotação @RequestMapping, o valor dessa anotação é necessário para se saber qual método se está acessando a partir da JSP, então, para acessar o método saveUser() a url será http://localhost:8080/spring-converter/users/save. Veja também, que no método editUser() temos na anotação @RequestMapping a instrução /edit/{id}. Esta instrução diz que o método acessado será o editUser() e que este método receberá como parâmetro um id. Para o Spring receber este id no controller, devemos informar a ele através da anotação @PathVariable, informando também o tipo deste id, que neste caso é um Long.
As anotações @ModelAttribute recebem no controller o objeto enviado da página e a anotação @Valid recebe o objeto da página como também indica que ele deve ser validado pela classe de validação. No método saveUser() usamos o parametro BindindResult para testar se existe algum erro no formulário da JSP, quando enviado para o controller, se houver, a execução do método entra no if() do método e retorna para a página de cadastro exibindo as mensagens de erro da classe de validação.
package com.mballem.springconverter.web.controller;
import com.mballem.springconverter.dao.IProfileDao;
import com.mballem.springconverter.dao.IUserDao;
import com.mballem.springconverter.entity.Profile;
import com.mballem.springconverter.entity.User;
import com.mballem.springconverter.web.converter.BirthdatePropertyEditor;
import com.mballem.springconverter.web.converter.ProfilePropertyEditor;
import com.mballem.springconverter.web.validator.UserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* http://www.mballem.com/
*/
@Controller
@RequestMapping(value = "/users")
public class UserController {
@Autowired
private IUserDao userDao;
@Autowired
private IProfileDao profileDao;
@Autowired
private UserValidator userValidator;
@InitBinder
protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
binder.setValidator(userValidator);
binder.registerCustomEditor(
Profile.class,
new ProfilePropertyEditor(profileDao)
);
binder.registerCustomEditor(
Date.class,
new BirthdatePropertyEditor(new SimpleDateFormat("dd/MM/yyyy"))
);
}
//index é encaminhado para este metodo e abre a pagina addUser
@RequestMapping(value = "/add", method = RequestMethod.GET)
public ModelAndView showForm(@ModelAttribute("user") User user,
BindingResult result) {
//adiciona a pagina destino e a lista de profiles
return new ModelAndView("userAdd", modelProfiles());
}
//quando clicar no botao salvar, vem para este metodo
@RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView saveUser(@Valid User user, BindingResult result) {
if (result.hasFieldErrors()) {
return new ModelAndView("userAdd", modelProfiles());
}
userDao.saveOrUpdate(user);
//redireciona para a pagina que lista os users
return new ModelAndView("redirect:/users");
}
//lista os users
@RequestMapping(method = RequestMethod.GET)
public ModelAndView listUsers() {
//recupera do db todos os users
Map model = new HashMap();
model.put("users", userDao.findAll());
//adiciona no objeto ModelAndView a pagina que deve abrir e
// a lista de users
return new ModelAndView("userList", model);
}
//editar usuario
@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public ModelAndView editUser(@PathVariable("id") Long id) {
//recupera o user atraves do id vindo da pagina
User user = userDao.find(id);
//cria um objeto ModelAndView e adiciona no construtor
// a pagina que deve abrir
ModelAndView modelAndView = new ModelAndView("userAdd");
//envia para a pagina os dados do objeto user
modelAndView.addObject("user", user);
//envia para a pagina a lista de profiles
modelAndView.addAllObjects(modelProfiles());
return modelAndView;
}
//remover usuario
@RequestMapping(value = "/remove/{id}", method = RequestMethod.GET)
public String removeUser(@PathVariable("id") Long id, Model model) {
//recupera do database o user atraves do id vindo da jsp
User user = userDao.find(id);
//teste simples para saber se o usuario existe no db
if (user.getId() != null) {
userDao.delete(user);
}
//retorna para a pagina que lista os users
return "redirect:/users";
}
private Map modelProfiles() {
//recupera os profiles do database
Map mapProfile = new HashMap();
mapProfile.put("profiles", profileDao.findAll());
return mapProfile;
}
}
3. Configuração do Spring Framework
Vamos agora configurar o Spring Framework e o arquivo web.xml (Listagem 11).
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>spring-converter</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description>Configuracao do Spring</description>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Na Listagem 12 temos o arquivo de configuração do dao, ou seja, onde configuramos a integração entre Spring 3 e Hibernate 4.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- Data Source Declaration -->
<bean id="dataSource"
>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/spring-converter"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
<!-- Configura a SessionFactory do Hibernate -->
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
>
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="packagesToScan" value="com.mballem.springconverter.entity"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Transaction Manager is defined -->
<bean id="transactionManager"
>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
Na Listagem 13 vamos configurar o MVC. Através desta configuração o Spring saberá o tipo de página que vai trabalhar e também o local onde estas páginas se encontram dentro do pacote da aplicação. Outra configuração que temos aqui é a criação do bean messageSource para o Spring saber onde encontrar o arquivo de propriedades com as mensagens de erro, por padrão o Spring procura pelo arquivo denominado ValidatorMessages e no pacote raiz da aplicação, ou resource no padrão Maven.
<?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.xsd">
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- Register the ValidationMessages.properties -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="ValidationMessages" />
</bean>
</beans>
Por fim, temos o arquivo que inicializa o Spring, chamado de spring-context.xml, conforme Listagem 14. As configurações neste arquivo são bem básicas, como, indicar ao framework o pacote base contendo as classes com as anotações do Spring, a anotação que indica que vamos usar o Spring MVC e também a importação dos arquivos spring-dao.xml e spring-web.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--Pacote base com as anotações do Spring -->
<context:component-scan base-package="com.mballem.springconverter"/>
<!-- Configures the @Controller programming model -->
<mvc:annotation-driven/>
<import resource="spring-web.xml"/>
<import resource="spring-dao.xml"/>
</beans>
5. Páginas JSP
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add User</title>
<style>
.error {
color: #ff0000;
font-style: italic;
}
</style>
</head>
<body>
<h1>Add User</h1>
<c:url var="viewUsersUrl" value="/users"/>
<a href="${viewUsersUrl}">Show All Users</a>
<br/>
<br/>
<table bgcolor="#f0f8ff">
<c:url var="saveUserUrl" value="/users/save"/>
<form:form name="userAdd" modelAttribute="user"
method="POST" action="${saveUserUrl}">
<form:hidden path="id"/>
<tr>
<td><form:label path="name">User name:</form:label></td>
<td><form:input path="name"/></td>
<td><form:errors path="name" cssClass="error"/></td>
</tr>
<tr>
<td><form:label path="birthDate">User date of birth:</form:label></td>
<td><form:input path="birthDate"/></td>
<td><form:errors path="birthDate" cssClass="error"/></td>
<tr/>
<tr>
<td><form:label path="profile">User profile:</form:label></td>
<td>
<form:select path="profile">
<form:option value="0" label="--- Select ---"/>
<form:options items="${profiles}"
itemValue="id" itemLabel="profile"/>
</form:select>
</td>
<td><form:errors path="profile" cssClass="error"/></td>
<tr/>
<tr>
<td><input type="submit" value="Save User"/></td>
</tr>
</form:form>
</table>
</body>
</html>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>All Users</title>
</head>
<body>
<h1>List Users</h1>
<a href="users/add">Add User</a>
<c:if test="${!empty users}">
<table>
<tr bgcolor=#f5f5dc>
<th align="left" width="150px">User Name</th>
<th align="left" width="150px">User Date of Birth</th>
<th align="left" width="100px">User Profile</th>
<th align="left" width="100px">Action</th>
</tr>
<c:forEach items="${users}" var="user" varStatus="id">
<tr bgcolor="#${id.count % 2 != 0 ? 'aaee88' : 'ffff00' }">
<td align="left" width="150px">
<c:out value="${user.name}"/>
</td>
<fmt:formatDate var="formatDate"
value="${user.birthDate}"
type="date"
pattern="dd/MM/yyyy" />
<td align="left" width="150px">
<c:out value="${formatDate}"/>
</td>
<td align="left" width="100px">
<c:out value='${user.profile.profile}'/>
</td>
<%--
é possivel usar a tag própria do Spring(spring:url)
ou a tag da Jstl(c:url)
--%>
<spring:url var="edit" value="users/edit/${user.id}"/>
<c:url var="remove" value="users/remove/${user.id}"/>
<td align="left" width="100px">
<a href='${edit}'>[edit]</a>|
<a href="${remove}">[remove]</a>
</td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Index</title></head>
<body>
<c:redirect url="/users/add"/>
<h1>Spring 3 MVC application conversor configuration.</h1>
</body>
</html>
Estrutura do Projeto
Saiba mais em:



