原文:Mkyong
Spring MVC 表单错误标签示例
在 Spring MVC 中,字段错误消息由与控制器相关联的验证器生成,您可以使用 < form:errors / > 标签在默认的 HTML“span”标签中呈现这些字段错误消息。举个例子,
1.验证器
验证器检查“用户名字段,如果为空,从资源包向控制器返回“必需.用户名错误消息。
//...
public class TextBoxValidator implements Validator{
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(
errors, "username", "required.username");
}
}
/*** required.username = username is required! ***/
2.显示字段错误
然后,您可以使用 <表单:errors / > 来呈现与“用户名字段相关联的错误消息。
<form:errors path="userName" cssClass="error" />
它将使用一个默认的“ span ”元素来呈现和封装错误消息,该元素包含一个 CSS 类“ error ”。
<span id="username.errors" class="error">username is required!</span>
Note
- path = " * "–显示与任何字段相关的所有错误消息。
- path = " username "–仅显示与“username”字段相关的错误消息。
3.自定义输出元素
出于某些原因,比如 CSS 格式化的目的,您可能需要用不同的元素来包含错误消息,而不是默认的“ span 标签。为此,只需在“元素属性中指定 prefer 元素:
<form:errors path="userName" cssClass="error" element="div" />
现在,它用一个“ div ”元素呈现并封装错误消息,该元素包含一个“错误”的 CSS 类。
<div id="username.errors" class="error">username is required!</div>
4.演示
下载源代码
Download it – SpringMVCForm-TextBox-Example.zip (9KB)form handling spring mvc
Spring MVC 表单处理注释示例
在本教程中,我们将向您展示如何在 Spring MVC web 应用程序中使用注释进行表单处理。
Note
This annotation-based example is converted from the last Spring MVC form handling XML-based example. So, please compare and spots the different.
1.简单表单控制器与@控制器
在基于 XML 的 Spring MVC web 应用程序中,您通过扩展SimpleFormController
类来创建表单控制器。
在基于注释的情况下,可以使用*@控制器*来代替。
简单表单控制器
public class CustomerController extends SimpleFormController{
//...
}
标注
@Controller
@RequestMapping("/customer.htm")
public class CustomerController{
//...
}
2.formBackingObject()vs request method。得到
在 SimpleFormController 中,可以在 formBackingObject() 方法中初始化命令对象进行绑定。在基于注释的方法中,您可以通过用**@ request mapping(method = request method)注释方法名来做同样的事情。**搞定)。
简单表单控制器
@Override
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
Customer cust = new Customer();
//Make "Spring MVC" as default checked value
cust.setFavFramework(new String []{
"Spring MVC"});
return cust;
}
标注
@RequestMapping(method = RequestMethod.GET)
public String initForm(ModelMap model){
Customer cust = new Customer();
//Make "Spring MVC" as default checked value
cust.setFavFramework(new String []{
"Spring MVC"});
//command object
model.addAttribute("customer", cust);
//return form view
return "CustomerForm";
}
3. onSubmit()诉 RequestMethod.POST
在 SimpleFormController 中,表单提交由 onSubmit() 方法处理。在基于注释的方法中,您可以通过用**@ request mapping(method = request method)注释方法名来做同样的事情。后)**。
简单表单控制器
@Override
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
Customer customer = (Customer)command;
return new ModelAndView("CustomerSuccess");
}
标注
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(
@ModelAttribute("customer") Customer customer,
BindingResult result, SessionStatus status) {
//clear the command object from the session
status.setComplete();
//return form success view
return "CustomerSuccess";
}
4.reference data()vs @ model attribute
在 SimpleFormController 中,通常通过 referenceData() 方法将引用数据放入模型中,以便表单视图可以访问它。在基于注释的方法中,您可以通过用 @ModelAttribute 注释方法名来做同样的事情。
简单表单控制器
@Override
protected Map referenceData(HttpServletRequest request) throws Exception {
Map referenceData = new HashMap();
//Data referencing for web framework checkboxes
List<String> webFrameworkList = new ArrayList<String>();
webFrameworkList.add("Spring MVC");
webFrameworkList.add("Struts 1");
webFrameworkList.add("Struts 2");
webFrameworkList.add("JSF");
webFrameworkList.add("Apache Wicket");
referenceData.put("webFrameworkList", webFrameworkList);
return referenceData;
}
弹簧的形状
<form:checkboxes items="${webFrameworkList}" path="favFramework" />
标注
@ModelAttribute("webFrameworkList")
public List<String> populateWebFrameworkList() {
//Data referencing for web framework checkboxes
List<String> webFrameworkList = new ArrayList<String>();
webFrameworkList.add("Spring MVC");
webFrameworkList.add("Struts 1");
webFrameworkList.add("Struts 2");
webFrameworkList.add("JSF");
webFrameworkList.add("Apache Wicket");
return webFrameworkList;
}
弹簧的形状
<form:checkboxes items="${webFrameworkList}" path="favFramework" />
5.initBinder()与@InitBinder
在 SimpleFormController 中,通过 initBinder() 方法定义绑定或注册自定义属性编辑器。在基于注释的方法中,您可以通过用 @InitBinder 注释方法名来做同样的事情。
简单表单控制器
protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
标注
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
来自验证
在 SimpleFormController 中,您必须通过 XML bean 配置文件注册验证器类并将其映射到控制器类,验证检查和工作流将自动执行。
在基于注释的情况下,您必须显式执行验证器,并手动在 @Controller 类中定义验证流。看到不同的:
简单表单控制器
<bean class="com.mkyong.customer.controller.CustomerController">
<property name="formView" value="CustomerForm" />
<property name="successView" value="CustomerSuccess" />
<!-- Map a validator -->
<property name="validator">
<bean class="com.mkyong.customer.validator.CustomerValidator" />
</property>
</bean>
标注
@Controller
@RequestMapping("/customer.htm")
public class CustomerController{
CustomerValidator customerValidator;
@Autowired
public CustomerController(CustomerValidator customerValidator){
this.customerValidator = customerValidator;
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(
@ModelAttribute("customer") Customer customer,
BindingResult result, SessionStatus status) {
customerValidator.validate(customer, result);
if (result.hasErrors()) {
//if validator failed
return "CustomerForm";
} else {
status.setComplete();
//form success
return "CustomerSuccess";
}
}
//...
完整示例
查看完整的@Controller 示例。
package com.mkyong.customer.controller;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;
import com.mkyong.customer.model.Customer;
import com.mkyong.customer.validator.CustomerValidator;
@Controller
@RequestMapping("/customer.htm")
public class CustomerController{
CustomerValidator customerValidator;
@Autowired
public CustomerController(CustomerValidator customerValidator){
this.customerValidator = customerValidator;
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(
@ModelAttribute("customer") Customer customer,
BindingResult result, SessionStatus status) {
customerValidator.validate(customer, result);
if (result.hasErrors()) {
//if validator failed
return "CustomerForm";
} else {
status.setComplete();
//form success
return "CustomerSuccess";
}
}
@RequestMapping(method = RequestMethod.GET)
public String initForm(ModelMap model){
Customer cust = new Customer();
//Make "Spring MVC" as default checked value
cust.setFavFramework(new String []{
"Spring MVC"});
//Make "Make" as default radio button selected value
cust.setSex("M");
//make "Hibernate" as the default java skills selection
cust.setJavaSkills("Hibernate");
//initilize a hidden value
cust.setSecretValue("I'm hidden value");
//command object
model.addAttribute("customer", cust);
//return form view
return "CustomerForm";
}
@ModelAttribute("webFrameworkList")
public List<String> populateWebFrameworkList() {
//Data referencing for web framework checkboxes
List<String> webFrameworkList = new ArrayList<String>();
webFrameworkList.add("Spring MVC");
webFrameworkList.add("Struts 1");
webFrameworkList.add("Struts 2");
webFrameworkList.add("JSF");
webFrameworkList.add("Apache Wicket");
return webFrameworkList;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
@ModelAttribute("numberList")
public List<String> populateNumberList() {
//Data referencing for number radiobuttons
List<String> numberList = new ArrayList<String>();
numberList.add("Number 1");
numberList.add("Number 2");
numberList.add("Number 3");
numberList.add("Number 4");
numberList.add("Number 5");
return numberList;
}
@ModelAttribute("javaSkillsList")
public Map<String,String> populateJavaSkillList() {
//Data referencing for java skills list box
Map<String,String> javaSkill = new LinkedHashMap<String,String>();
javaSkill.put("Hibernate", "Hibernate");
javaSkill.put("Spring", "Spring");
javaSkill.put("Apache Wicket", "Apache Wicket");
javaSkill.put("Struts", "Struts");
return javaSkill;
}
@ModelAttribute("countryList")
public Map<String,String> populateCountryList() {
//Data referencing for java skills list box
Map<String,String> country = new LinkedHashMap<String,String>();
country.put("US", "United Stated");
country.put("CHINA", "China");
country.put("SG", "Singapore");
country.put("MY", "Malaysia");
return country;
}
}
要使注释工作,您必须在 Spring 中启用组件自动扫描特性。
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.mkyong.customer.controller" />
<bean class="com.mkyong.customer.validator.CustomerValidator" />
<!-- Register the Customer.properties -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="com/mkyong/customer/properties/Customer" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
下载源代码
Download it – SpringMVC-Form-Handling-Annotation-Example.zip (12KB)
参考
- spring 2.5 中带注释的 web mvc 控制器
- Spring MVC 表单处理示例–XML 版本
- Spring MVC hello world 注释示例
- Spring MVC multi action controller 注释示例
Tags : annotation form spring mvc
Spring MVC 表单处理示例
在本教程中,我们将向您展示一个 Spring MVC 表单处理项目来完成以下工作:
- 表单值绑定 JSP 和模型。
- 表单验证并显示错误消息。
- 形成 POST/REDIRECT/GET 模式,并将消息添加到 flash 属性。
- CRUD 操作,用一个 HTML 表单添加、获取、更新和删除。
使用的技术:
- 弹簧 4.1.6 释放
- maven3
- 自举 3
- HSQLDB 驱动程序 2.3.2
- 回溯 1.1.3
- JDK 1.7
- JSTL 1.2
- Eclipse IDE
一个简单的用户管理项目,你可以通过 HTML 表单列出、创建、更新和删除一个用户。您还将看到如何执行表单验证并有条件地显示错误消息。该项目使用 Bootstrap 3 进行设计,数据存储在 HSQL 嵌入式数据库中。
URI 结构:
| 上呼吸道感染 | 方法 | 行动 |
| /用户 | 得到 | 列表,显示所有用户 |
| /用户 | 邮政 | 保存或更新用户 |
| /users/{id} | 得到 | 显示用户{id} |
| /用户/添加 | 得到 | 显示添加用户表单 |
| /users/{id}/update | 得到 | 显示{id}的更新用户表单 |
| /users/{id}/delete | 邮政 | 删除用户{id} |
Note
In the old days, before Spring 3.0, we use [SimpleFormController](http://web.archive.org/web/20190303052227/http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/web/portlet/mvc/SimpleFormController.html)
to do the form handling. As Spring 3.0, this class is deprecated in favor of Spring annotated @Controller.
Spring MVC 表单绑定
在开始本教程之前,您需要了解 Spring MVC 表单绑定是如何工作的。
1.1 在 controller 中,将对象添加到模型属性中。
@RequestMapping(value = "/users/add", method = RequestMethod.GET)
public String showAddUserForm(Model model) {
User user = new User();
model.addAttribute("userForm", user);
//...
}
1.2 在 HTML 表单中,你使用spring:form
标签,通过modelAttribute
绑定控制器对象。
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form method="post" modelAttribute="userForm" action="${userActionUrl}">
<form:input path="name" type="text" /> <!-- bind to user.name-->
<form:errors path="name" />
</form:form>
1.3 当 HTML 表单为“POST”时,通过@ModelAttribute
获取值。
@RequestMapping(value = "/users", method = RequestMethod.POST)
public String saveOrUpdateUser(@ModelAttribute("userForm") User user,
BindingResult result, Model model) {
//...
}
完成了。让我们开始教程。
1.项目目录
这是最终的项目目录结构。标准的 Maven 项目。
开发一个 Spring MVC 项目,需要spring-webmvc
。
pom.xml
<project
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.form</groupId>
<artifactId>spring-mvc-form</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringMVC + Form Handling Example</name>
<properties>
<jdk.version>1.7</jdk.version>
<spring.version>4.1.6.RELEASE</spring.version>
<hsqldb.version>2.3.2</hsqldb.version>
<logback.version>1.1.3</logback.version>
<jcl.slf4j.version>1.7.12</jcl.slf4j.version>
<jstl.version>1.2</jstl.version>
<servletapi.version>3.1.0</servletapi.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${
spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${
spring.version}</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${
spring.version}</version>
</dependency>
<!-- HyperSQL DB -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${
hsqldb.version}</version>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${
jcl.slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${
logback.version}</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${
jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${
servletapi.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${
jdk.version}</source>
<target>${
jdk.version}</target>
</configuration>
</plugin>
<!-- embedded Jetty server, for testing -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.11.v20150529</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webApp>
<contextPath>/spring-mvc-form</contextPath>
</webApp>
</configuration>
</plugin>
<!-- configure Eclipse workspace -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<wtpversion>2.0</wtpversion>
<wtpContextName>spring-mvc-form</wtpContextName>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.控制器
Spring @Controller
类向你展示如何通过@ModelAttribute
绑定表单值,添加表单验证器,将消息添加到 flash 属性中,填充下拉列表和复选框的值等等。阅读评论,不言自明。
UserController.java
package com.mkyong.form.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.mkyong.form.model.User;
import com.mkyong.form.service.UserService;
import com.mkyong.form.validator.UserFormValidator;
@Controller
public class UserController {
private final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@Autowired
UserFormValidator userFormValidator;
//Set a form validator
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(userFormValidator);
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model) {
logger.debug("index()");
return "redirect:/users";
}
// list page
@RequestMapping(value = "/users", method = RequestMethod.GET)
public String showAllUsers(Model model) {
logger.debug("showAllUsers()");
model.addAttribute("users", userService.findAll());
return "users/list";
}
// save or update user
// 1\. @ModelAttribute bind form value
// 2\. @Validated form validator
// 3\. RedirectAttributes for flash value
@RequestMapping(value = "/users", method = RequestMethod.POST)
public String saveOrUpdateUser(@ModelAttribute("userForm") @Validated User user,
BindingResult result, Model model,
final RedirectAttributes redirectAttributes) {
logger.debug("saveOrUpdateUser() : {}", user);
if (result.hasErrors()) {
populateDefaultModel(model);
return "users/userform";
} else {
// Add message to flash scope
redirectAttributes.addFlashAttribute("css", "success");
if(user.isNew()){
redirectAttributes.addFlashAttribute("msg", "User added successfully!");
}else{
redirectAttributes.addFlashAttribute("msg", "User updated successfully!");
}
userService.saveOrUpdate(user);
// POST/REDIRECT/GET
return "redirect:/users/" + user.getId();
// POST/FORWARD/GET
// return "user/list";
}
}
// show add user form
@RequestMapping(value = "/users/add", method = RequestMethod.GET)
public String showAddUserForm(Model model) {
logger.debug("showAddUserForm()");
User user = new User();
// set default value
user.setName("mkyong123");
user.setEmail("[email protected]");
user.setAddress("abc 88");
user.setNewsletter(true);
user.setSex("M");
user.setFramework(new ArrayList<String>(Arrays.asList("Spring MVC", "GWT")));
user.setSkill(new ArrayList<String>(Arrays.asList("Spring", "Grails", "Groovy")));
user.setCountry("SG");
user.setNumber(2);
model.addAttribute("userForm", user);
populateDefaultModel(model);
return "users/userform";
}
// show update form
@RequestMapping(value = "/users/{id}/update", method = RequestMethod.GET)
public String showUpdateUserForm(@PathVariable("id") int id, Model model) {
logger.debug("showUpdateUserForm() : {}", id);
User user = userService.findById(id);
model.addAttribute("userForm", user);
populateDefaultModel(model);
return "users/userform";
}
// delete user
@RequestMapping(value = "/users/{id}/delete", method = RequestMethod.POST)
public String deleteUser(@PathVariable("id") int id,
final RedirectAttributes redirectAttributes) {
logger.debug("deleteUser() : {}", id);
userService.delete(id);
redirectAttributes.addFlashAttribute("css", "success");
redirectAttributes.addFlashAttribute("msg", "User is deleted!");
return "redirect:/users";
}
// show user
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public String showUser(@PathVariable("id") int id, Model model) {
logger.debug("showUser() id: {}", id);
User user = userService.findById(id);
if (user == null) {
model.addAttribute("css", "danger");
model.addAttribute("msg", "User not found");
}
model.addAttribute("user", user);
return "users/show";
}
private void populateDefaultModel(Model model) {
List<String> frameworksList = new ArrayList<String>();
frameworksList.add("Spring MVC");
frameworksList.add("Struts 2");
frameworksList.add("JSF 2");
frameworksList.add("GWT");
frameworksList.add("Play");
frameworksList.add("Apache Wicket");
model.addAttribute("frameworkList", frameworksList);
Map<String, String> skill = new LinkedHashMap<String, String>();
skill.put("Hibernate", "Hibernate");
skill.put("Spring", "Spring");
skill.put("Struts", "Struts");
skill.put("Groovy", "Groovy");
skill.put("Grails", "Grails");
model.addAttribute("javaSkillList", skill);
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
model.addAttribute("numberList", numbers);
Map<String, String> country = new LinkedHashMap<String, String>();
country.put("US", "United Stated");
country.put("CN", "China");
country.put("SG", "Singapore");
country.put("MY", "Malaysia");
model.addAttribute("countryList", country);
}
}
4.表单验证器
4.1 Spring 验证器示例。
UserFormValidator.java
package com.mkyong.form.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.mkyong.form.model.User;
import com.mkyong.form.service.UserService;
@Component
public class UserFormValidator implements Validator {
@Autowired
@Qualifier("emailValidator")
EmailValidator emailValidator;
@Autowired
UserService userService;
@Override
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.userForm.name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "NotEmpty.userForm.email");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address", "NotEmpty.userForm.address");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty.userForm.password");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "confirmPassword","NotEmpty.userForm.confirmPassword");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "sex", "NotEmpty.userForm.sex");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "country", "NotEmpty.userForm.country");
if(!emailValidator.valid(user.getEmail())){
errors.rejectValue("email", "Pattern.userForm.email");
}
if(user.getNumber()==null || user.getNumber()<=0){
errors.rejectValue("number", "NotEmpty.userForm.number");
}
if(user.getCountry().equalsIgnoreCase("none")){
errors.rejectValue("country", "NotEmpty.userForm.country");
}
if (!user.getPassword().equals(user.getConfirmPassword())) {
errors.rejectValue("confirmPassword", "Diff.userform.confirmPassword");
}
if (user.getFramework() == null || user.getFramework().size() < 2) {
errors.rejectValue("framework", "Valid.userForm.framework");
}
if (user.getSkill() == null || user.getSkill().size() < 3) {
errors.rejectValue("skill", "Valid.userForm.skill");
}
}
}
validation.properties
NotEmpty.userForm.name = Name is required!
NotEmpty.userForm.email = Email is required!
NotEmpty.userForm.address = Address is required!
NotEmpty.userForm.password = Password is required!
NotEmpty.userForm.confirmPassword = Confirm password is required!
NotEmpty.userForm.sex = Sex is required!
NotEmpty.userForm.number = Number is required!
NotEmpty.userForm.country = Country is required!
Valid.userForm.framework = Please select at least two frameworks!
Valid.userForm.skill = Please select at least three skills!
Diff.userform.confirmPassword = Passwords do not match, please retype!
Pattern.userForm.email = Invalid Email format!
要运行 Spring 验证器,通过@InitBinder
添加验证器,并用@Validated
注释模型
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(userFormValidator);
}
@RequestMapping(value = "/users", method = RequestMethod.POST)
public String saveOrUpdateUser(... @Validated User user,
...) {
//...
}
或者手动运行。
@RequestMapping(value = "/users", method = RequestMethod.POST)
public String saveOrUpdateUser(... User user,
...) {
userFormValidator.validate(user, result);
//...
}
5.HTML 表单
所有的 HTML 表单都是 css 样式,使用 Bootstrap 框架,并使用 Spring form 标签进行显示和表单绑定。
5.1 用户列表。
list.jsp
<%@ page session="false"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html lang="en">
<jsp:include page="../fragments/header.jsp" />
<body>
<div class="container">
<c:if test="${not empty msg}">
<div class="alert alert-${css} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>${
msg}</strong>
</div>
</c:if>
<h1>All Users</h1>
<table class="table table-striped">
<thead>
<tr>
<th>#ID</th>
<th>Name</th>
<th>Email</th>
<th>framework</th>
<th>Action</th>
</tr>
</thead>
<c:forEach var="user" items="${users}">
<tr>
<td>
${
user.id}
</td>
<td>${
user.name}</td>
<td>${
user.email}</td>
<td>
<c:forEach var="framework" items="${user.framework}"
varStatus="loop">
${
framework}
<c:if test="${not loop.last}">,</c:if>
</c:forEach>
</td>
<td>
<spring:url value="/users/${user.id}" var="userUrl" />
<spring:url value="/users/${user.id}/delete" var="deleteUrl" />
<spring:url value="/users/${user.id}/update" var="updateUrl" />
<button class="btn btn-info"
onclick="location.href='${userUrl}'">Query</button>
<button class="btn btn-primary"
onclick="location.href='${updateUrl}'">Update</button>
<button class="btn btn-danger"
onclick="this.disabled=true;post('${deleteUrl}')">Delete</button>
</td>
</tr>
</c:forEach>
</table>
</div>
<jsp:include page="../fragments/footer.jsp" />
</body>
</html>
show.jsp
<%@ page session="false"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html lang="en">
<jsp:include page="../fragments/header.jsp" />
<div class="container">
<c:if test="${not empty msg}">
<div class="alert alert-${css} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>${
msg}</strong>
</div>
</c:if>
<h1>User Detail</h1>
<br />
<div class="row">
<label class="col-sm-2">ID</label>
<div class="col-sm-10">${
user.id}</div>
</div>
<div class="row">
<label class="col-sm-2">Name</label>
<div class="col-sm-10">${
user.name}</div>
</div>
<div class="row">
<label class="col-sm-2">Email</label>
<div class="col-sm-10">${
user.email}</div>
</div>
<div class="row">
<label class="col-sm-2">Address</label>
<div class="col-sm-10">${
user.address}</div>
</div>
<div class="row">
<label class="col-sm-2">Newsletter</label>
<div class="col-sm-10">${
user.newsletter}</div>
</div>
<div class="row">
<label class="col-sm-2">Web Frameworks</label>
<div class="col-sm-10">${
user.framework}</div>
</div>
<div class="row">
<label class="col-sm-2">Sex</label>
<div class="col-sm-10">${
user.sex}</div>
</div>
<div class="row">
<label class="col-sm-2">Number</label>
<div class="col-sm-10">${
user.number}</div>
</div>
<div class="row">
<label class="col-sm-2">Country</label>
<div class="col-sm-10">${
user.country}</div>
</div>
<div class="row">
<label class="col-sm-2">Skill</label>
<div class="col-sm-10">${
user.skill}</div>
</div>
</div>
<jsp:include page="../fragments/footer.jsp" />
</body>
</html>
userform.jsp
<%@ page session="false"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html lang="en">
<jsp:include page="../fragments/header.jsp" />
<div class="container">
<c:choose>
<c:when test="${userForm['new']}">
<h1>Add User</h1>
</c:when>
<c:otherwise>
<h1>Update User</h1>
</c:otherwise>
</c:choose>
<br />
<spring:url value="/users" var="userActionUrl" />
<form:form class="form-horizontal" method="post"
modelAttribute="userForm" action="${userActionUrl}">
<form:hidden path="id" />
<spring:bind path="name">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<form:input path="name" type="text" class="form-control"
id="name" placeholder="Name" />
<form:errors path="name" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="email">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<form:input path="email" class="form-control"
id="email" placeholder="Email" />
<form:errors path="email" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="password">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<form:password path="password" class="form-control"
id="password" placeholder="password" />
<form:errors path="password" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="confirmPassword">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">confirm Password</label>
<div class="col-sm-10">
<form:password path="confirmPassword" class="form-control"
id="password" placeholder="password" />
<form:errors path="confirmPassword" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="address">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Address</label>
<div class="col-sm-10">
<form:textarea path="address" rows="5" class="form-control"
id="address" placeholder="address" />
<form:errors path="address" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="newsletter">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Newsletter</label>
<div class="col-sm-10">
<div class="checkbox">
<label>
<form:checkbox path="newsletter" id="newsletter" />
</label>
<form:errors path="newsletter" class="control-label" />
</div>
</div>
</div>
</spring:bind>
<spring:bind path="framework">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Web Frameworks</label>
<div class="col-sm-10">
<form:checkboxes path="framework" items="${frameworkList}"
element="label class='checkbox-inline'" />
<br />
<form:errors path="framework" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="sex">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Sex</label>
<div class="col-sm-10">
<label class="radio-inline">
<form:radiobutton path="sex" value="M" /> Male
</label>
<label class="radio-inline">
<form:radiobutton path="sex" value="F" /> Female
</label> <br />
<form:errors path="sex" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="number">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Number</label>
<div class="col-sm-10">
<form:radiobuttons path="number" items="${numberList}"
element="label class='radio-inline'" />
<br />
<form:errors path="number" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="country">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Country</label>
<div class="col-sm-5">
<form:select path="country" class="form-control">
<form:option value="NONE" label="--- Select ---" />
<form:options items="${countryList}" />
</form:select>
<form:errors path="country" class="control-label" />
</div>
<div class="col-sm-5"></div>
</div>
</spring:bind>
<spring:bind path="skill">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Java Skills</label>
<div class="col-sm-5">
<form:select path="skill" items="${javaSkillList}"
multiple="true" size="5" class="form-control" />
<form:errors path="skill" class="control-label" />
</div>
<div class="col-sm-5"></div>
</div>
</spring:bind>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<c:choose>
<c:when test="${userForm['new']}">
<button type="submit" class="btn-lg btn-primary pull-right">Add
</button>
</c:when>
<c:otherwise>
<button type="submit" class="btn-lg btn-primary pull-right">Update
</button>
</c:otherwise>
</c:choose>
</div>
</div>
</form:form>
</div>
<jsp:include page="../fragments/footer.jsp" />
</body>
</html>
6.数据库的东西
6.1 创建一个表,并插入一些测试数据。
create-db.sql
CREATE TABLE users (
id INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 100, INCREMENT BY 1) PRIMARY KEY,
name VARCHAR(30),
email VARCHAR(50),
address VARCHAR(255),
password VARCHAR(20),
newsletter BOOLEAN,
framework VARCHAR(500),
sex VARCHAR(1),
NUMBER INTEGER,
COUNTRY VARCHAR(10),
SKILL VARCHAR(500)
);
insert-data.sql
INSERT INTO users (name, email, framework) VALUES ('mkyong', '[email protected]', 'Spring MVC, GWT');
INSERT INTO users (name, email) VALUES ('alex', '[email protected]', 'Spring MVC, GWT');
INSERT INTO users (name, email) VALUES ('joel', '[email protected]', 'Spring MVC, GWT');
6.2 启动一个 HSQLDB 嵌入式数据库,创建一个数据源和 jdbcTemplate。
SpringDBConfig.java
package com.mkyong.form.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
public class SpringDBConfig {
@Autowired
DataSource dataSource;
@Bean
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource);
}
@Bean
public DataSource getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setName("testdb")
.setType(EmbeddedDatabaseType.HSQL)
.addScript("db/sql/create-db.sql")
.addScript("db/sql/insert-data.sql").build();
return db;
}
}
6.3 用户对象。
user.java
package com.mkyong.form.model;
import java.util.List;
public class User {
// form:hidden - hidden value
Integer id;
// form:input - textbox
String name;
// form:input - textbox
String email;
// form:textarea - textarea
String address;
// form:input - password
String password;
// form:input - password
String confirmPassword;
// form:checkbox - single checkbox
boolean newsletter;
// form:checkboxes - multiple checkboxes
List<String> framework;
// form:radiobutton - radio button
String sex;
// form:radiobuttons - radio button
Integer number;
// form:select - form:option - dropdown - single select
String country;
// form:select - multiple=true - dropdown - multiple select
List<String> skill;
//Check if this is for New of Update
public boolean isNew() {
return (this.id == null);
}
//...
}
7.服务和一体行动
UserService.java
package com.mkyong.form.service;
import java.util.List;
import com.mkyong.form.model.User;
public interface UserService {
User findById(Integer id);
List<User> findAll();
void saveOrUpdate(User user);
void delete(int id);
}
UserServiceImpl.java
package com.mkyong.form.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mkyong.form.dao.UserDao;
import com.mkyong.form.model.User;
@Service("userService")
public class UserServiceImpl implements UserService {
UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User findById(Integer id) {
return userDao.findById(id);
}
@Override
public List<User> findAll() {
return userDao.findAll();
}
@Override
public void saveOrUpdate(User user) {
if (findById(user.getId())==null) {
userDao.save(user);
} else {
userDao.update(user);
}
}
@Override
public void delete(int id) {
userDao.delete(id);
}
}
UserDao.java
package com.mkyong.form.dao;
import java.util.List;
import com.mkyong.form.model.User;
public interface UserDao {
User findById(Integer id);
List<User> findAll();
void save(User user);
void update(User user);
void delete(Integer id);
}
UserDaoImpl.java
package com.mkyong.form.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import com.mkyong.form.model.User;
@Repository
public class UserDaoImpl implements UserDao {
NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
public void setNamedParameterJdbcTemplate(
NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
@Override
public User findById(Integer id) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", id);
String sql = "SELECT * FROM users WHERE id=:id";
User result = null;
try {
result = namedParameterJdbcTemplate
.queryForObject(sql, params, new UserMapper());
} catch (EmptyResultDataAccessException e) {
// do nothing, return null
}
return result;
}
@Override
public List<User> findAll() {
String sql = "SELECT * FROM users";
List<User> result = namedParameterJdbcTemplate.query(sql, new UserMapper());
return result;
}
@Override
public void save(User user) {
KeyHolder keyHolder = new GeneratedKeyHolder();
String sql = "INSERT INTO USERS(NAME, EMAIL, ADDRESS, PASSWORD, NEWSLETTER, FRAMEWORK, SEX, NUMBER, COUNTRY, SKILL) "
+ "VALUES ( :name, :email, :address, :password, :newsletter, :framework, :sex, :number, :country, :skill)";
namedParameterJdbcTemplate.update(sql, getSqlParameterByModel(user), keyHolder);
user.setId(keyHolder.getKey().intValue());
}
@Override
public void update(User user) {
String sql = "UPDATE USERS SET NAME=:name, EMAIL=:email, ADDRESS=:address, "
+ "PASSWORD=:password, NEWSLETTER=:newsletter, FRAMEWORK=:framework, "
+ "SEX=:sex, NUMBER=:number, COUNTRY=:country, SKILL=:skill WHERE id=:id";
namedParameterJdbcTemplate.update(sql, getSqlParameterByModel(user));
}
@Override
public void delete(Integer id) {
String sql = "DELETE FROM USERS WHERE id= :id";
namedParameterJdbcTemplate.update(sql, new MapSqlParameterSource("id", id));
}
private SqlParameterSource getSqlParameterByModel(User user) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("id", user.getId());
paramSource.addValue("name", user.getName());
paramSource.addValue("email", user.getEmail());
paramSource.addValue("address", user.getAddress());
paramSource.addValue("password", user.getPassword());
paramSource.addValue("newsletter", user.isNewsletter());
// join String
paramSource.addValue("framework", convertListToDelimitedString(user.getFramework()));
paramSource.addValue("sex", user.getSex());
paramSource.addValue("number", user.getNumber());
paramSource.addValue("country", user.getCountry());
paramSource.addValue("skill", convertListToDelimitedString(user.getSkill()));
return paramSource;
}
private static final class UserMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setFramework(convertDelimitedStringToList(rs.getString("framework")));
user.setAddress(rs.getString("address"));
user.setCountry(rs.getString("country"));
user.setNewsletter(rs.getBoolean("newsletter"));
user.setNumber(rs.getInt("number"));
user.setPassword(rs.getString("password"));
user.setSex(rs.getString("sex"));
user.setSkill(convertDelimitedStringToList(rs.getString("skill")));
return user;
}
}
private static List<String> convertDelimitedStringToList(String delimitedString) {
List<String> result = new ArrayList<String>();
if (!StringUtils.isEmpty(delimitedString)) {
result = Arrays.asList(StringUtils.delimitedListToStringArray(delimitedString, ","));
}
return result;
}
private String convertListToDelimitedString(List<String> list) {
String result = "";
if (list != null) {
result = StringUtils.arrayToCommaDelimitedString(list.toArray());
}
return result;
}
}
8.弹簧配置
混合 Spring XML 和 JavaConfig。
SpringWebConfig.java
package com.mkyong.form.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc
@Configuration
@ComponentScan({
"com.mkyong.form.web", "com.mkyong.form.service", "com.mkyong.form.dao",
"com.mkyong.form.exception", "com.mkyong.form.validator" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource rb = new ResourceBundleMessageSource();
rb.setBasenames(new String[] {
"messages/messages", "messages/validation" });
return rb;
}
}
spring-web-servlet.xml
<beans
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scan the JavaConfig -->
<context:component-scan base-package="com.mkyong.form.config" />
</beans>
web.xml
<web-app
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>Spring3 MVC Application</display-name>
<servlet>
<servlet-name>spring-web</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-web</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/jsp/error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/jsp/error.jsp</location>
</error-page>
<error-page>
<location>/WEB-INF/views/jsp/error.jsp</location>
</error-page>
</web-app>
9.演示
下载项目并键入mvn jetty:run
$ mvn jetty:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building SpringMVC + Form Handling Example 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
//...
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.
**9.1 列出所有用户。**http://localhost:8080/spring-MVC-form/users
**9.2 添加用户。**http://localhost:8080/spring-MVC-form/users/add
9.3 表单验证。
**9.4 新增用户。**http://localhost:8080/spring-MVC-form/users
**9.5 删除用户。**http://localhost:8080/spring-MVC-form/users/104/delete
下载源代码
Download it - spring4-form-handle-example.zip (80 kb)Github - TBD
参考
Spring MVC 处理程序拦截器示例
Spring MVC 允许您通过处理程序拦截器来拦截 web 请求。处理程序拦截器必须实现 HandlerInterceptor 接口,该接口包含三个方法:
- preHandle()–在处理程序执行前调用,返回一个布尔值,“true”:继续处理程序执行链;“false”,停止执行链并返回。
- post handle()–在处理程序执行后调用,允许在将 ModelAndView 对象呈现到视图页面之前对其进行操作。
- after completion()–在完成请求完成后调用。很少使用,找不到任何用例。
在本教程中,您将创建两个处理程序拦截器来展示 HandlerInterceptor 的用法。
- execute time interceptor–拦截 web 请求,记录控制器执行时间。
- 维护拦截器–拦截 web 请求,检查当前时间是否在维护时间之间,如果是,则重定向到维护页面。
Note
It’s recommended to extend the HandlerInterceptorAdapter for the convenient default implementations.
1.ExecuteTimeInterceptor
截取控制器执行前后的时间,记录执行的开始和结束时间,保存到已有的截取控制器的 modelAndView 中,以便以后显示。
文件:ExecuteTimeInterceptor.java
package com.mkyong.common.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{
private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
//after the handler is executed
public void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception {
long startTime = (Long)request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
//modified the exisitng modelAndView
modelAndView.addObject("executeTime",executeTime);
//log it
if(logger.isDebugEnabled()){
logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
}
}
}
2.维护拦截器
在控制器执行前拦截,检查当前时间是否在维护时间之间,如果是则重定向到维护页面;否则继续执行链。
文件:MaintenanceInterceptor.java
package com.mkyong.common.interceptor;
import java.util.Calendar;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class MaintenanceInterceptor extends HandlerInterceptorAdapter{
private int maintenanceStartTime;
private int maintenanceEndTime;
private String maintenanceMapping;
public void setMaintenanceMapping(String maintenanceMapping) {
this.maintenanceMapping = maintenanceMapping;
}
public void setMaintenanceStartTime(int maintenanceStartTime) {
this.maintenanceStartTime = maintenanceStartTime;
}
public void setMaintenanceEndTime(int maintenanceEndTime) {
this.maintenanceEndTime = maintenanceEndTime;
}
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
Calendar cal = Calendar.getInstance();
int hour = cal.get(cal.HOUR_OF_DAY);
if (hour >= maintenanceStartTime && hour <= maintenanceEndTime) {
//maintenance time, send to maintenance page
response.sendRedirect(maintenanceMapping);
return false;
} else {
return true;
}
}
}
3.启用处理程序拦截器
要启用它,请将您的处理程序拦截器类放入处理程序映射“拦截器”属性中。
<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.5.xsd">
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome.htm">welcomeController</prop>
</props>
</property>
<property name="interceptors">
<list>
<ref bean="maintenanceInterceptor" />
<ref bean="executeTimeInterceptor" />
</list>
</property>
</bean>
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
<property name="interceptors">
<list>
<ref bean="executeTimeInterceptor" />
</list>
</property>
</bean>
<bean id="welcomeController"
class="com.mkyong.common.controller.WelcomeController" />
<bean class="com.mkyong.common.controller.MaintenanceController" />
<bean id="executeTimeInterceptor"
class="com.mkyong.common.interceptor.ExecuteTimeInterceptor" />
<bean id="maintenanceInterceptor"
class="com.mkyong.common.interceptor.MaintenanceInterceptor">
<property name="maintenanceStartTime" value="23" />
<property name="maintenanceEndTime" value="24" />
<property name="maintenanceMapping" value="/SpringMVC/maintenance.htm" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
下载源代码
Download it –SpringMVC-HandlerInterceptor-Example.zip (8 KB)
参考
- HandlerInterceptorAdapter 文档
- 处理接收器文档
- ControllerClassNameHandlerMapping 示例
- SimpleUrlHandlerMapping 示例
Tags : interceptor spring mvc
用 AbstractWizardFormController 处理多页表单
在上一个 Spring MVC 表单处理示例中,我们将向您介绍如何使用 SimpleFormController 来处理单页表单提交,这非常简单明了。
但是,有时,您可能需要处理"向导表单,这需要将表单处理成多页,并要求用户逐页填写表单。在这种向导表单情况下,主要关心的是如何存储模型数据(由用户填写的数据)并将其带到多个页面上?
SAbstractWizardFormController
幸运的是,Spring MVC 自带了AbstractWizardFormController类来轻松处理这个向导表单。在本教程中,我们将向您展示如何使用AbstractWizardFormController类来跨多个页面存储和携带表单数据,应用验证并在最后一页显示表单数据。
1.向导表单页面
本演示共 5 页,按以下顺序进行:
[User] --> WelcomePage --> Page1 --> Page2 --> Page3 --> ResultPage
使用AbstractWizardFormController,页面顺序由提交按钮的“名称”决定:
- _finish:完成向导表单。
- _cancel:取消向导表单。
- _targetx:移动到目标页面,其中 x 是从零开始的页面索引。例如 _target0 、 _target1 等。
1。WelcomePage.jsp
一个欢迎页面,带有一个超链接来启动向导表单过程。
<html>
<body>
<h2>Handling multipage forms in Spring MVC</h2>
Click here to start playing -
<a href="user.htm">AbstractWizardFormController example</a>
</body>
</html>
2。Page1Form.jsp
第 1 页,有一个“用户名”文本框,显示错误信息(如果有的话),并包含 2 个提交按钮,其中:
- _ target 1–移至第 2 页。
- _ cancel–取消向导表单流程,并将其移至取消页面
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<style>
.error {
color: #ff0000;
}
.errorblock {
color: #000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body>
<h2>Page1Form.jsp</h2>
<form:form method="POST" commandName="userForm">
<form:errors path="*" cssClass="errorblock" element="div" />
<table>
<tr>
<td>Username :</td>
<td><form:input path="userName" />
</td>
<td><form:errors path="userName" cssClass="error" />
</td>
</tr>
<tr>
<tr>
<td colspan="3"><input type="submit" value="Next"
name="_target1" /> <input type="submit" value="Cancel"
name="_cancel" /></td>
</tr>
</table>
</form:form>
</body>
</html>
3。Page2Form.jsp
第 2 页,有一个“密码”字段,显示错误信息(如果有的话),并包含 3 个提交按钮,其中:
- _ target 0–移至第 1 页。
- _ target 2–移至第 3 页。
- _ cancel–取消向导表单流程,并将其移至取消页面
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<style>
.error {
color: #ff0000;
}
.errorblock {
color: #000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body>
<h2>Page2Form.jsp</h2>
<form:form method="POST" commandName="userForm">
<form:errors path="*" cssClass="errorblock" element="div" />
<table>
<tr>
<td>Password :</td>
<td><form:password path="password" />
</td>
<td><form:errors path="password" cssClass="error" />
</td>
</tr>
<tr>
<tr>
<td colspan="3"><input type="submit" value="Previous"
name="_target0" /> <input type="submit" value="Next"
name="_target2" /> <input type="submit" value="Cancel"
name="_cancel" /></td>
</tr>
</table>
</form:form>
</body>
</html>
4。Page3Form.jsp
第 3 页,有一个“备注”文本框,显示错误信息(如果有),并包含 3 个提交按钮,其中:
- _ target 1–移至第 2 页。
- _ finish–完成向导表单流程,并将其移至完成页面。
- _ cancel–取消向导表单流程,并将其移至取消页面。
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<style>
.error {
color: #ff0000;
}
.errorblock {
color: #000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body>
<h2>Page3Form.jsp</h2>
<form:form method="POST" commandName="userForm">
<form:errors path="*" cssClass="errorblock" element="div" />
<table>
<tr>
<td>Remark :</td>
<td><form:input path="remark" />
</td>
<td><form:errors path="remark" cssClass="error" />
</td>
</tr>
<tr>
<tr>
<td colspan="3"><input type="submit" value="Previous"
name="_target1" /> <input type="submit" value="Finish"
name="_finish" /> <input type="submit" value="Cancel"
name="_cancel" /></td>
</tr>
</table>
</form:form>
</body>
</html>
5。ResultForm.jsp
显示前 3 页收集的所有表单数据。
<html>
<body>
<h2>ResultForm.jsp</h2>
<table>
<tr>
<td>UserName :</td>
<td>${
user.userName}</td>
</tr>
<tr>
<td>Password :</td>
<td>${
user.password}</td>
</tr>
<tr>
<td>Remark :</td>
<td>${
user.remark}</td>
</tr>
</table>
</body>
</html>
2.模型
创建一个模型类来存储表单数据。
文件:User.java
package com.mkyong.common.model;
public class User{
String userName;
String password;
String remark;
//getter and setter methods
}
3.AbstractWizardFormController
扩展AbstractWizardFormController,只需覆盖以下方法
- process finish–当用户点击名为 _finish 的提交按钮时触发。
- process cancel–当用户点击名为 _cancel 的提交按钮时触发。
- formBackingObject–使用“User”模型类将所有表单数据存储在多个页面中。
文件:UserController.java
package com.mkyong.common.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractWizardFormController;
import com.mkyong.common.model.User;
import com.mkyong.common.validator.UserValidator;
public class UserController extends AbstractWizardFormController{
public UserController(){
setCommandName("userForm");
}
@Override
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
return new User();
}
@Override
protected ModelAndView processFinish(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
//Get the data from command object
User user = (User)command;
System.out.println(user);
//where is the finish page?
return new ModelAndView("ResultForm", "user", user);
}
@Override
protected ModelAndView processCancel(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
//where is the cancel page?
return new ModelAndView("WelcomePage");
}
}
一个简单的控制器返回一个" WelcomePage "视图。
文件:WelcomeController.java
package com.mkyong.common.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
public class WelcomeController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("WelcomePage");
}
}
4.多页/向导表单验证
在SimpleFormController
中,创建一个验证器类,将所有的验证逻辑放在 validate() 方法中,并将验证器装饰性地注册到简单表单控制器中。
但是,在AbstractWizardFormController中有点不同。首先,创建一个验证器类,以及每个页面的验证方法,如下所示:
文件:UserValidator.java
package com.mkyong.common.validator;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.mkyong.common.model.User;
public class UserValidator implements Validator{
@Override
public boolean supports(Class clazz) {
//just validate the User instances
return User.class.isAssignableFrom(clazz);
}
//validate page 1, userName
public void validatePage1Form(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
"required.userName", "Field name is required.");
}
//validate page 2, password
public void validatePage2Form(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password",
"required.password", "Field name is required.");
}
//validate page 3, remark
public void validatePage3Form(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "remark",
"required.remark", "Field name is required.");
}
@Override
public void validate(Object target, Errors errors) {
validatePage1Form(target, errors);
validatePage2Form(target, errors);
validatePage3Form(target, errors);
}
}
File:user . Properties–存储错误消息的属性
required.userName = Username is required!
required.password = Password is required!
required.remark = Remark is required!
并且,在向导表单控制器(UserController.java
)中,通过手动调用验证器来覆盖 validatePage() (不再有像简单表单控制器那样的声明)。
见UserController.java的更新版本。
public class UserController extends AbstractWizardFormController{
//other methods, see above
@Override
protected void validatePage(Object command, Errors errors, int page) {
UserValidator validator = (UserValidator) getValidator();
//page is 0-indexed
switch (page) {
case 0: //if page 1 , go validate with validatePage1Form
validator.validatePage1Form(command, errors);
break;
case 1: //if page 2 , go validate with validatePage2Form
validator.validatePage2Form(command, errors);
break;
case 2: //if page 3 , go validate with validatePage3Form
validator.validatePage3Form(command, errors);
break;
}
}
}
在 validatePage() 方法中,使用一个“开关函数来确定哪个页面正在调用,并将其与相应的验证器相关联。该页面的索引为 0。
5.弹簧配置
声明向导表单控制器(UserController.java
),按正确的顺序排列所有页面,并注册一个验证器。
<bean class="com.mkyong.common.controller.UserController" >
<property name="pages">
<list>
<!-- follow sequence -->
<value>Page1Form</value> <!-- page1, _target0 -->
<value>Page2Form</value> <!-- page2, _target1 -->
<value>Page3Form</value> <!-- page3, _target2 -->
</list>
</property>
<property name="validator">
<bean class="com.mkyong.common.validator.UserValidator" />
</property>
</bean>
Note
In the “pages” property, the order of the list value is used to define the sequence of the page in the wizard form.
查看完整示例:
<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.5.xsd">
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.mkyong.common.controller.WelcomeController" />
<bean class="com.mkyong.common.controller.UserController" >
<property name="pages">
<list>
<!-- follow sequence -->
<value>Page1Form</value> <!-- page1 -->
<value>Page2Form</value> <!-- page2 -->
<value>Page3Form</value> <!-- page3 -->
</list>
</property>
<property name="validator">
<bean class="com.mkyong.common.validator.UserValidator" />
</property>
</bean>
<!-- Register User.properties for validation error message -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="User" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
5.演示
网址:http://localhost:8080/spring MVC/welcome . htm
1。WelcomePage.jsp,点击链接,移动到 Page1Form.jsp**。**
2。Page1Form.jsp,包含一个“用户名”文本框和两个按钮:
- “下一步”按钮–移动到 Page2Form.jsp。
- “取消”按钮–移至 WelcomePage.jsp
如果提交表单时“用户名”为空,则显示错误消息。
3。Page2Form.jsp,包含一个“密码”字段和 3 个按钮:
- “上一页”按钮–移至 Page1Form.jsp。
- “下一步”按钮–移动到 Page3Form.jsp。
- “取消”按钮–移至 WelcomePage.jsp。
4。Page3Form.jsp,包含一个“备注”文本框和 3 个按钮:
- “上一页”按钮–移至 Page2Form.jsp。
- “完成”按钮–移动到 ResultForm.jsp。
- “取消”按钮–移至 WelcomePage.jsp。
5。ResultForm.jsp,显示所有表单的数据。
下载源代码
Download it – SpringMVC-MultiPage-Form-Handling-Example.zip (12KB)
参考
Tags : spring mvc