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 Listuser; //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 IProfileDaodao; 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 IUserDaouserDao; @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: