0.学习准备
- 参考资料:
参考书籍《Spring MVC学习指南》(第二版)(基础学习)
《Spring实战》(第四版)(综合运用)
参考视频:
尚硅谷《Spring MVC》(补充知识) - 简单目录:
- 数据绑定简介与使用
- Spring表单标签库
- 表单标签库使用实例
- Spring的表单标签库使用的不是很多,所以了解一些就行。
主要的前台页面还是以JSTL和EL表达式为主。
1.数据绑定的简介与使用
- 在前面的例子中的控制器类方法中:
例子中(详见上一节笔记)并没有为ProductForm对象填充任何数据,但是在后面却可以在ProductForm对象中获取到用户输入到表单中的信息。@RequestMapping(value="/product-save") public String SaveProduct(ProductForm productForm,RedirectAttributes redirectAttributes){ logger.info("调用SaveProduct方法处理请求"); Product product =new Product(); product.setName(productForm.getName()); product.setDiscription(productForm.getDiscription()); try{ product.setPrice(Float.parseFloat(productForm.getPrice())); }catch(Exception e){ } long id=productService.add(product); redirectAttributes.addFlashAttribute("product",product); return "redirect:/print_product/"+id; //重定向到下面的处理方法 }
SpringMVC自动将用户输入到表单中的数据同ProductForm对象绑定,这就是数据绑定。 - 数据绑定是将用户输入绑定到领域模型的一种特性。
类型为String的HTTP请求参数可以用于填充各种不同类型的对象属性。 - 上面的ProductForm是一个表单类,作用只是为了转换price的类型。
因为数据绑定可以绑定各种类型,所以就不需要表单类了。 - 上述的代码可以简化如下:
@RequestMapping(value="/product-save") public String SaveProduct2(Product product,RedirectAttributes redirectAttributes){ long id=productService.add(product); redirectAttributes.addFlashAttribute("product",product); return "redirect:/print_product/"+id; }
- 以上只是简单的数据绑定,更加精细的数据绑定需要用Spring表单标签库提供的属性来加以控制。
2.Spring表单标签库
表单标签库简介与使用
- Spring表单标签库包含了可以用在JSP页面中渲染HTML元素的标签。
这些标签在提供原有的HTML标签功能的基础上,还提供了一些和数据绑定相关的属性。 - Spring的表单标签库和SpringMVC框架是集成在一起的,因此它们可以直接使用命令对象(command object)和其他由控制器处理的数据对象。
- 在JSP页面中引入表单标签库:
在JSP页面开头处声明
即可,然后使用<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:xxx>
来使用各渲染标签。 - Spring表单标签库的标签:
- 下面详细介绍这些标签的属性及使用。
(spring表单标签库提供的属性都是可选的)
form,input,password,hidden标签
1)form表单标签:
- 表单标签用于渲染HTML表单。在其中需要使用其他表单标签。
- form标签有HTML表单的各项属性(action,method等),还提供了一些额外的属性:
- 最重要的是commandName标签,定义了模型(Model)属性(键)的名称。
其中包含了一个backing object
,这个对象的属性将用来填充表单。
如果表单设置了该属性,那么在跳转到该视图(初始化该JSP)的请求方法中一定要有对应的模型属性。Model.addAttribute("key",Object);
- 下面使用代码说明:
BookAddForm视图中的表单标签:
初始化请求方法:... <form:form commandName="book" action="book_save" method="post"> ... </form> ...
@RequestMapping(value="/book_input") public String inputBook(Model model){ ... Model("book",new Book()); //一定要设置,否则会因表单标签找不到backing object而报错 return "BookAddForm"; }
- 一般来说仍需要使用HTML表单提供的action和method属性。
- 关于htmlEscape:
是否转义特殊字符,默认为true,若要设置不对特殊字符进行转义,需设置为false。
2)input标签:
- input标签渲染了
<input type="text"/>
元素。
除了input标签原有的属性外,还有以下扩展属性: - 其中最重要的是path属性,可以用于将这个标签绑定
backing object
的一个属性。
如:
会被渲染成:<form:form commandName="book" action="book_save" method="post"> <form:input id="bookname" path="bookname" cssErrorClass="errorBox"/> </form>
将该标签和book的bookname属性绑定。<input type="text" id="bookname" name="bookname"/>
(如果该属性不存在,也不会出错,只是无法进行数据绑定) - cssErrorClass属性一般情况下不起作用,只有绑定属性(bookname)的输入验证错误并且使用该表单重新显示用户输入,input标签会被渲染如下:
<input type="text" id="bookname" name="bookname" class="errorBox"/>
- 可以绑定嵌套对象:
(如果将author对象嵌入在book对象中)<form:input id="authorname" path="author.name"/>
该input标签将绑定到book中的author对象的name属性
3)password标签:
- 渲染了
<input type="password"/>
元素。
和password元素相似,只不过多了一个showPassword属性。
除了HTML原有的属性外,提供了如下的属性: - 示例如下:
会被渲染成如下的HTML标签:<form:password id="pwd" path="password" cssClass="normal"/>
<input type="password" name="password" class="normal"/>
4)hidden标签:
- 渲染
<input type="hidden">
元素。
因为不可见,所以没有CSS相关的属性。
除了原有的属性外,提供了如下两个属性: - 示例:
会被渲染成:<form:hidden path="bookId"/>
<input type="hidden" name="bookId"/>
textarea,checkbox(es),radiobutton(s)标签
1)textarea标签:
- 渲染了HTML的textarea元素。
除了原有的属性之外,还提供了以下的属性: - 示例:
<form:textarea path="discripe" tabindex="4" rows="5" cols="80"/>
2)checkbox标签:
- 渲染一个
<input type="checkbox"/>
元素,多选框。 - 除了原有的属性,还有下列额外属性:
- 示例:
<form:checkbox path="booktype" value="type1"/> <form:checkbox path="booktype" value="type2"/> <form:checkbox path="booktype" value="type3"/>
3)radiobutton标签:
- 渲染一个
<input type="radio"/>
元素,单选框。 - 除了原有的属性,还有下列额外属性:
- 示例:
<form:radiobutton path="sex" value="男"/> <form:radiobutton path="sex" value="女"/>
4)checkboxes标签:
- 渲染多个
<input type="checkbox"/>
标签,多选框。 - 提供了如下属性:
- items属性提供了用于生成多选框的数据集合。
itemLabel属性定义了数据集合中的对象的某一属性,用来做字面量。
itemValue属性定义了数据集合中的对象的某一属性,用来做选取得到的值。 - 示例如下:
<form:checkboxes path="typeIds" items="${typelist}" itemLabel="typeName" itemValue="typeId"/>
5)radiobuttons标签:
- 渲染了多个
<input type="radio"/>
元素,单选框,用法和属性checkboxes标签类似。 - 提供了下列属性:
- 使用示例:
<form:radiobuttons path="sex" items="${sexlist}"/>
select,option(s),errors标签
1)select标签:
- 渲染了一个HTML的select元素。
被渲染的元素可以来自items属性,也可以来自option或者options标签。 - 提供了如下属性:
- 使用示例如下(通过items元素绑定数据)
<form:select id="type" items=${typelist} itemLabel="typeName" itemValue="typeId"/>
2)option标签:
- 渲染了select元素中的一个HTML的option元素。
- 只提供了一些和css相关的渲染属性:
- 使用示例:(通过items和option标签绑定数据)
或者也可以使用HTML的option元素:<form:select id="type" items=${typelist} itemLabel="typeName" itemValue="typeId"> <form:option value="0">请选择类型</form:option> </form:select>
<option value="0">请选择类型<option>
3)options标签:
- options标签生成一个HTML的option元素列表。
- 除了HTML属性外还提供了如下属性:
- 使用示例:(option和options绑定数据)
<form:select path="type"> <option/>请选择人员 <form:options items="${typelist}" itemLabel="typeName" itemValue="typeId"/> </form:select>
4)errors标签:
- 渲染一个或者多个HTML的span元素,每个span元素中都包含一个字段错误。
可以用于显示一个特定字段的错误,或者所有字段的错误。 - 提供了如下属性:(cssClss和cssStyle未列出)
- 使用示例:
显示所有字段的错误:
显示与某个<form:errors path="*"/>
from bacjing object
对象的属性(author)相关的字段错误:<form:errors path="author"/>
3.数据绑定示例
示例说明:
一个关于书籍管理的简单应用(不涉及数据库层)
有实体类Book,Book中又包含了Catgoty类对象。
有列出书目,添加新书以及编辑数目功能。
基本配置及模型类
- Category类:
//书的类型 public class Category implements Serializable{ private static final long serialVersionUID = 1558731101438330815L; private int id; //类型ID private String name; //类型名 public Category() { } public Category(int id,String name) { this.id=id; this.name=name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Category [id=" + id + ", name=" + name + "]"; } }
- Book类:
public class Book implements Serializable{ private static final long serialVersionUID = 8761144554202637953L; private long id; private String isbn; //国际标准书号 private String title; //书名 private Category category; //类型 private String author; //作者 public Book() { } public Book(long id, String isbn, String title, Category category, String author) { super(); this.id = id; this.isbn = isbn; this.title = title; this.category = category; this.author = author; } //get,set与toString方法 }
配置文件如下:
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.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"> <context:component-scan base-package="Controller3"></context:component-scan> <context:component-scan base-package="Service3"></context:component-scan> <mvc:annotation-driven></mvc:annotation-driven> <mvc:resources location="/css/**" mapping="/css"></mvc:resources> <mvc:resources location="/*.html" mapping="/"></mvc:resources> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp3/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
web.xml文件如下:
<?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_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/jsp3/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
- 创建相应的BookController类和BookService接口,基本结构如下:
添加与查看书籍功能
1)初始化添加页面:
- BookService接口中添加方法如下:
public interface BookService { List<Category> getAllCategories(); Category getCategoryById(int id); }
BookService的实现类BookServiceImpl代码如下:
@Service public class BookServiceImpl implements BookService{ private List<Category> categories=new ArrayList<Category>(); private List<Book> booklist=new ArrayList<Book>(); public BookServiceImpl() { categories.add(new Category(1,"技术")); categories.add(new Category(2,"小说")); categories.add(new Category(3,"旅游")); categories.add(new Category(4,"生活")); categories.add(new Category(5,"历史")); } @Override public List<Category> getAllCategories() { return categories; } @Override public Category getCategoryById(int id) { for(Category category:categories){ if(id==category.getId()){ return category; } } return null; } }
控制器类BookController代码如下:
@Controller public class BookController { @Autowired BookService bookService; private static final Log logger=LogFactory.getLog(BookController.class); //初始化添加书籍视图 @RequestMapping("/book_input") public String inputBook(Model model){ List<Category> categories=bookService.getAllCategories(); model.addAttribute("categories",categories); model.addAttribute("book",new Book()); return "AddBook"; } }
- WEB-INF/jsp3/AddBook.jsp代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>添加书籍</title> </head> <body> <form:form commandName="book" action="book_save" method="post"> <label for="title">书籍名字:</label> <form:input path="title" id="title"/><br/> <label for="author">作者:</label> <form:input path="author" id="author"/><br/> <label for="isbn">国际标准书号(ISBN):</label> <form:input path="isbn" id="isbn"/><br/> <label for="category">书籍类型:</label> <form:select path="category.id" id="category" items="${categories}" itemLabel="name" itemValue="id"/><br/> <input type="reset" id="reset"> <input type="submit" id="submit"> </form:form> </body> </html>
- 测试,初始化成功:
- 使用Spring表单标签库时出现了一些问题,详见Spring解决方案一问题8。
2)添加表单处理方法及列表视图:
- 添加BookService接口的方法:
public interface BookService { List<Category> getAllCategories(); Category getCategoryById(int id); List<Book> getAllBooks(); long getNextId(); Book saveBook(Book book); }
在实现类BookServiceImpl中添加实现方法:
... @Override public List<Book> getAllBooks() { return booklist; } @Override public Book saveBook(Book book) { book.setId(getNextId()); booklist.add(book); return book; } @Override synchronized public long getNextId() { long id=0l; for(Book book:booklist){ if(book.getId()>id){ id=book.getId(); } } return id+1; } ...
- 在BookController控制器类中添加一个处理添加书籍的方法:
添加savaBook处理方法如下:
添加book列表的处理方法如下:@RequestMapping("/book_save") public String saveBook(@ModelAttribute Book book){ logger.info("调用saveBook请求处理方法"); Category category=bookService.getCategoryById(book.getCategory().getId()); book.setCategory(category); bookService.saveBook(book); return "redirect:/book_list"; }
@RequestMapping("/book_list") public String bookList(Model model){ logger.info("调用bookList请求处理方法"); List<Book> books=bookService.getAllBooks(); model.addAttribute("booklist",books); return "BookList"; }
需要用到JSTL,所以添加JSTL的pom.xml依赖:
<!--old jstl--><!-- <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>--> <!--jstl--> <dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-spec</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-impl</artifactId> <version>1.2.5</version> </dependency>
- 创建BookList.jsp视图:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>书籍列表</title> </head> <body> <table> <tr> <th>书籍名</th> <th>作者</th> <th>类型</th> <th>ISBN</th> <th>操作</th> </tr> <c:forEach items="${booklist}" var="book"> <tr> <td>${book.title}</td> <td>${book.author}</td> <td>${book.category.name}</td> <td>${book.isbn}</td> <td><a href="book_edit/${book.id}">编辑</a></td> </tr> </c:forEach> </table> </body>
- 测试:
添加页面:
添加后跳转到书籍列表:
控制台输出:
编辑书籍功能
1)跳转到编辑表单并显示:
- 添加BookService接口方法:
Book getBookById(long id);
- 添加BookServiceImpl中的实现方法:
@Override public Book getBookById(long id) { for(Book book:booklist){ if(book.getId()==id){ return book; } } return null; }
- 点击编辑时的请求处理方法(编辑表单的)
在控制器类中添加请求处理方法editBook方法:@RequestMapping("/book_edit/{id}") public String editBook(Model model,@PathVariable long id){ logger.info("调用editBook请求处理方法"); Book book=bookService.getBookById(id); List<Category> categories=bookService.getAllCategories(); model.addAttribute("categories",categories); model.addAttribute("book",book); return "EditBook"; }
- 创建EditBook.jsp视图:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>编辑书籍</title> </head> <body> <!--注意这里的action地址需要使用绝对路径,否则会变成/book_edit/book_update,或者也可以修改@RequestMapping--> <form:form commandName="book" action="/springmvc/book_update" method="post"> <form:hidden path="id"/> <label for="title">书籍名字:</label> <form:input path="title" id="title"/><br/> <label for="author">作者:</label> <form:input path="author" id="author"/><br/> <label for="isbn">国际标准书号(ISBN):</label> <form:input path="isbn" id="isbn"/><br/> <label for="category">书籍类型:</label> <form:select path="category.id" id="category" items="${categories}" itemLabel="name" itemValue="id"/><br/> <input type="reset" id="reset"> <input type="submit" id="submit"> </form:form> </body> </html>
- 测试:
添加书籍并跳转到书籍列表:
点击编辑:
控制台输出:
2)更新书籍信息:
- 添加BookService接口方法:
void updateBook(Book book);
- 添加BookServiceImpl类实现方法:
@Override public void updateBook(Book book) { int count=booklist.size(); for(int i=0;i<count;i++){ Book book1=booklist.get(i); if(book1.getId()==book.getId()){ booklist.set(i,book); } } }
- 添加控制器请求处理方法:
//更新书籍处理方法 @RequestMapping("/book_update") public String updateBook(Book book){ logger.info("调用saveBook请求处理方法"); Category category=bookService.getCategoryById(book.getCategory().getId()); book.setCategory(category); bookService.updateBook(book); return "redirect:/book_list"; }
- 测试:
更新前列表:
点击编辑:
点击提交,更新后列表:
控制台输出:
4.测试代码总结
- 接口BookService代码:
public interface BookService { List<Category> getAllCategories(); Category getCategoryById(int id); List<Book> getAllBooks(); long getNextId(); Book saveBook(Book book); Book getBookById(long id); void updateBook(Book book); }
接口实现类BookServiceImpl代码:
@Service public class BookServiceImpl implements BookService{ private List<Category> categories=new ArrayList<Category>(); private List<Book> booklist=new ArrayList<Book>(); public BookServiceImpl() { categories.add(new Category(1,"技术")); categories.add(new Category(2,"小说")); categories.add(new Category(3,"旅游")); categories.add(new Category(4,"生活")); categories.add(new Category(5,"历史")); } @Override public List<Category> getAllCategories() { return categories; } @Override public Category getCategoryById(int id) { for(Category category:categories){ if(id==category.getId()){ return category; } } return null; } @Override public List<Book> getAllBooks() { return booklist; } @Override public Book saveBook(Book book) { book.setId(getNextId()); booklist.add(book); return book; } @Override synchronized public long getNextId() { long id=0l; for(Book book:booklist){ if(book.getId()>id){ id=book.getId(); } } return id+1; } @Override public Book getBookById(long id) { for(Book book:booklist){ if(book.getId()==id){ return book; } } return null; } @Override public void updateBook(Book book) { int count=booklist.size(); for(int i=0;i<count;i++){ Book book1=booklist.get(i); if(book1.getId()==book.getId()){ booklist.set(i,book); } } } }
控制器类BookController代码:
package Controller3; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import Model.Book; import Model.Category; import Service3.BookService; import Service3.BookServiceImpl; @Controller public class BookController { @Autowired BookService bookService; private static final Log logger=LogFactory.getLog(BookController.class); //初始化添加书籍页面 @RequestMapping("/book_input") public String inputBook(Model model){ logger.info("调用inputBook请求处理方法"); List<Category> categories=bookService.getAllCategories(); model.addAttribute("categories",categories); model.addAttribute("book",new Book()); return "AddBook"; } //添加书籍处理 @RequestMapping("/book_save") public String saveBook(@ModelAttribute Book book){ logger.info("调用saveBook请求处理方法"); Category category=bookService.getCategoryById(book.getCategory().getId()); book.setCategory(category); bookService.saveBook(book); return "redirect:/book_list"; } //书籍列表初始化处理方法 @RequestMapping("/book_list") public String bookList(Model model){ logger.info("调用bookList请求处理方法"); List<Book> books=bookService.getAllBooks(); model.addAttribute("booklist",books); return "BookList"; } //编辑表单初始化 @RequestMapping("/book_edit/{id}") public String editBook(Model model,@PathVariable long id){ logger.info("调用editBook请求处理方法"); Book book=bookService.getBookById(id); List<Category> categories=bookService.getAllCategories(); model.addAttribute("categories",categories); model.addAttribute("book",book); return "EditBook"; } //更新书籍处理方法 @RequestMapping("/book_update") public String updateBook(Book book){ logger.info("调用saveBook请求处理方法"); Category category=bookService.getCategoryById(book.getCategory().getId()); book.setCategory(category); bookService.updateBook(book); return "redirect:/book_list"; } }
最后更新: 2018年05月22日 21:51
原始链接: https://zjxkenshine.github.io/2018/05/20/SpringMVC学习(三):数据绑定与表单标签库/