【Spring MVC学习笔记】4:数据绑定和表单标签库,Spring MVC的综合使用例子

数据绑定和表单标签库

数据绑定即将用户输入绑定到领域对象,它主要有两个好处:

①数据绑定可以直接绑定到模型对象,不必使用属性全是字符串的Form对象。
②输入验证失败时,会重新生成一个HTML表单回填。

对于第二点(回填)还不是很理解,暂且记下来。

数据绑定于JSP页面上常常会用到Spring MVC的表单标签库,在Struts2里也是有这样的标签库,在Spring MVC中使用的是:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

Spring MVC的综合使用例子

还原了一下书上的这个例子,这个例子同时将前面学的Spring MVC的知识一起用了起来,做完之后对Spring MVC有了一个完整的把握。

解决Spring MVC表单POST提交的乱码问题

类似Struts2地,Spring MVC的编码也是ISO-8859-1,可以在web.xml前部添加一个编码过滤器:

<filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

即可解决这一问题。

JSTL的引入

这部分和Spring MVC没关系,只是书上的例子用到了方便的JSTL。JSP标准标签库需要添加两个jar包,在这里下载解压就能找到,放在lib目录下并引入即可:
这里写图片描述

web.xml和dispatcher-servlet.xml

除了添加了前面的编码过滤器之外,这两个文件的配置都和前面学的时候一样,没改动。

org.domain下的Book类的属性

领域对象类都应当实现Serializable接口,必须具有无参构造器,必须为所有属性提供getter和setter方法,后面的Category类也是这样。

private long id;//图书号
private String isbn;//isbn号
private String title;//书名
private Category category;//类别
private String author;//作者

org.domain下的Category类的属性

private int id;//书籍分类号
private String name;//类别名称

org.service下的BookService接口

package org.service;

import org.domain.Book;
import org.domain.Category;

import java.util.List;

//服务层接口,提供用于为Book服务的方法
public interface BookService {
    //获取所有的书籍种类
    List<Category> getAllCategories();

    //按书籍种类号获取种类对象
    Category getCategory(int id);

    //获取所有的书
    List<Book> getAllBooks();

    //添加一本书(一个新的Book对象)
    Book save(Book book);

    //更新一本书,传入一个修改过信息的Book对象(id是标识它的,不会变)
    Book update(Book book);

    //根据书的id获取一本书
    Book get(long id);

    //当要添加一本新书时,需要调用这个方法获取一个允许的为其分配的id
    //因为只是模拟,没有操作数据库,所以需要这样的方法;如果数据库里设置了自增,可以不使用这种方式
    long getNextId();
}

org.service.imp下的BookServiceImp类

package org.service.imp;

import org.domain.Book;
import org.domain.Category;
import org.service.BookService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

//使能被用作DI给控制器类
@Service
//书籍服务的一种实现(即服务层接口的一个实现类)
//书上提到这种实现不是线程安全的,先不去管这些..
public class BookServiceImp implements BookService {
    //服务类组合这两个List对象,来使得后面的操作能进行
    //即不使用数据库,模拟书籍的种类和所有书籍都是保存在内存中(这个实现类的成员)
    private List<Category> categories;
    private List<Book> books;

    //在构造该类的对象时初始化这两个List,并往里面装一些数据
    public BookServiceImp() {
        //类别们
        this.categories = new ArrayList<>();
        Category category1 = new Category(1, "计算机");
        Category category2 = new Category(2, "旅游");
        Category category3 = new Category(3, "健康");
        categories.add(category1);
        categories.add(category2);
        categories.add(category3);

        //具体书籍们
        books = new ArrayList<>();
        Book book1 = new Book(1L, "isbn号1", "书名1", category1, "作者1");
        Book book2 = new Book(2L, "isbn号2", "书名2", category2, "作者2");
        Book book3 = new Book(3L, "isbn号3", "书名3", category3, "作者3");
        books.add(book1);
        books.add(book2);
        books.add(book3);
    }

    //获取所有类别
    @Override
    public List<Category> getAllCategories() {
        return this.categories;
    }

    //获取指定类别号的类别
    @Override
    public Category getCategory(int id) {
        for (Category category : this.categories) {
            if (id == category.getId()) {
                return category;
            }
        }
        return null;//找不到
    }

    //获取所有的书
    @Override
    public List<Book> getAllBooks() {
        return this.books;
    }

    //新增一本书
    @Override
    public Book save(Book book) {
        //获取一个可用的书籍id
        Long id = this.getNextId();
        //其它的信息已经在里面了,只要赋予它这个可用的id就行了
        book.setId(id);
        //添加到books里面
        this.books.add(book);
        return book;
    }

    //传入一个修改过信息的Book对象,以更新这本书的信息
    @Override
    public Book update(Book book) {
        //即只要在books里面找到这本书(通过它的id),然后更新一下就可以了
        //books里有多少本书
        int bookCount = this.books.size();
        //然后就可以按下标遍历,以便按下标设置
        for (int i = 0; i < bookCount; i++) {
            //如果id相同,说明要更新的就是这本书
            if (book.getId() == this.books.get(i).getId()) {
                //更新它,因为传入的已经是更新好的book对象,直接更改里面存的对象引用就行了
                books.set(i, book);
                return book;
            }
        }
        return null;//找不到
    }

    //按书号获取一本书
    @Override
    public Book get(long id) {
        //遍历找到id一样的书
        for (Book book : this.books) {
            if (id == book.getId()) {
                return book;
            }
        }
        return null;//没找到
    }

    //获取新添加一本书时可用的id,这个实现是当前最大的id加上1
    @Override
    public long getNextId() {
        //初始化为最小的0
        long id = 0L;//书上提示这个资源在多线程环境下需要加锁
        //遍历找到当前books里面最大的id
        for (Book book : this.books) {
            //便利到的书籍的id
            long bookId = book.getId();
            //如果比当前找到的还大
            if (bookId > id) {
                id = bookId;//替换
            }
        }
        //循环结束后的id就是最大的,返回它加1
        //考虑到books为空时,第一本书id应该是1L,所以这个局部变量前面初始化为0L
        return id + 1;
    }
}

org.controller下的BookController类

package org.controller;

import org.domain.Book;
import org.domain.Category;
import org.service.BookService;
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 java.util.List;

@Controller
//书籍相关的控制器类
public class BookController {
    //注入一个实现了书籍的服务接口的类的对象
    @Autowired
    private BookService bookService;

    //用户要访问表单页
    @RequestMapping(value = "/book_input")
    public String inputBook(Model model) {
        //获取所有的类别号,表单页上要用
        List<Category> categories = bookService.getAllCategories();
        //放进Model对象
        model.addAttribute("categories", categories);
        //放一个新创建的Book对象到Model对象里,以和表单关联(作它的领域对象)
        model.addAttribute("book", new Book());
        return "BookAddForm";
    }

    //用户要访问显示所有书的页面
    @RequestMapping(value = "/book_list")
    public String ListBooks(Model model) {
        //获取所有书的列表
        List<Book> books = bookService.getAllBooks();
        //放进Model对象
        model.addAttribute("books", books);
        return "BookList";
    }

    //用户在显示所有书的页面点击某本书,要编辑那本书,即要进入编辑书的页面
    //使用路径变量,以表示要编辑的是哪本书
    @RequestMapping(value = "/book_edit/{id}")
    public String editBook(Model model, @PathVariable long id) {
        //因为书的类别可能也会被编辑,所以类别List要使用,所以要拿出来放进Model对象
        List<Category> categories = bookService.getAllCategories();
        model.addAttribute("categories", categories);
        //这本书的原来的信息要拿出来,放进Model对象里并和表单关联
        Book book = bookService.get(id);
        model.addAttribute("book", book);
        return "BookEditForm";
    }

    //用户要添加一本新图书
    @RequestMapping(value = "/book_save")
    //这里用@ModelAttribute修饰这个book是Model中key为"book"的对象
    public String saveBook(@ModelAttribute Book book) {
        //根据选择的类别号,完善Book对象中类别号对象的name属性
        //因为提交时候只提交了Book对象的类别号对象的id属性,这是不完整的,所以要用这个id重新查
        Category category = bookService.getCategory(book.getCategory().getId());
        //把查到的完整的类别对象设置给Book对象
        book.setCategory(category);
        //调用服务层方法添加这本新图书
        bookService.save(book);
        //重定向到book_list页面,这样才能调用前面的ListBooks方法查出那些书来
        return "redirect:/book_list";
    }

    //用户要更新一本图书
    @RequestMapping(value = "/book_update")
    //这里用@ModelAttribute修饰这个book是Model中key为"book"的对象
    public String updateBook(@ModelAttribute Book book) {
        //和添加图书的情况一样,更新图书只是选择了类别的id,需要查出完整的类别对象来给这个Book对象
        Category category = bookService.getCategory(book.getCategory().getId());
        book.setCategory(category);
        //调用服务层方法更新这本书
        bookService.update(book);
        //重定向到book_list页面,这样才能调用前面的ListBooks方法查出那些书来
        return "redirect:/book_list";
    }

}

BookList.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用JSTL(JSP标准标签库)要添加两个jar包到lib目录下--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>所有书籍</title>
</head>
<body>
<h1>所有书籍</h1>
<%--用${pageContext.request.contextPath}完善请求路径--%>
<a href="${pageContext.request.contextPath}/book_input">添加新书</a>
<table>
    <tr>
        <th>类别</th>
        <th>书名</th>
        <th>ISBN号</th>
        <th>作者</th>
        <th>操作</th>
    </tr>
    <%--遍历放入Model对象里的books列表,每次跌倒元素记作book--%>
    <c:forEach items="${books}" var="book">
        <tr>
                <%--对象图导航来显示各种属性,这里显示的是这本书的类别属性的名称属性--%>
            <td>${book.category.name}</td>
            <td>${book.title}</td>
            <td>${book.isbn}</td>
            <td>${book.author}</td>
                <%--编辑时,路径参数是要编辑的这本书的id--%>
            <td><a href="${pageContext.request.contextPath}/book_edit/${book.id}">编辑</a></td>
        </tr>
    </c:forEach>
</table>
</body>
</html>

BookAddForm.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用Spring MVC的表单标签库--%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%--JSTL标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>新增图书的表单页</title>
</head>
<body>
<%--commandName属性指示表单关联的模型对象的名称--%>
<form:form commandName="book" action="book_save" method="post">
    <fieldset>
        <legend>添加一本新书</legend>
        <p>
            <label for="cate">类别:</label>
                <%--path属性指示这个表单控件所关联的表单对象的属性(可以嵌套)--%>
                <%--itemLabel是选择控件的外显,itemValue是内涵,肯定是name外显而id内涵了--%>
            <form:select id="cate" items="${categories}" path="category.id" itemLabel="name" itemValue="id"/>
        </p>
        <p>
            <label for="ttl">书名:</label>
            <%--form:input就相当于type是text的input标签--%>
            <form:input id="ttl" path="title"/>
        </p>
        <p>
            <label for="atr">作者:</label>
            <form:input id="atr" path="author"/>
        </p>
        <p>
            <label for="isb">ISBN号:</label>
            <form:input id="isb" path="isbn"/>
        </p>
        <p>
            <input type="submit" value="添加">
        </p>
    </fieldset>
</form:form>
</body>
</html>

BookEditForm.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用Spring MVC的表单标签库--%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%--JSTL标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>编辑图书信息的表单页</title>
</head>
<body>
<%--commandName属性指示表单关联的模型对象的名称--%>
<%--注意!因为更新(Update)是在编辑(Edit)页面,这里action如果不加'/'的话将变成提交到/book_edit/book_update--%>
<form:form commandName="book" action="/book_update" method="post">
    <fieldset>
        <legend>编辑一本已经存在的书的信息</legend>
            <%--因为书已经取出来到Model里,其中id是不应该修改的,所以用hidden--%>
        <form:hidden path="id"/>
        <p>
            <label for="cate">类别:</label>
                <%--path属性指示这个表单控件所关联的表单对象的属性(可以嵌套)--%>
                <%--itemLabel是选择控件的外显,itemValue是内涵,肯定是name外显而id内涵了--%>
            <form:select id="cate" path="category.id" items="${categories}" itemLabel="name" itemValue="id"/>
        </p>
        <p>
            <label for="ttl">书名:</label>
                <%--form:input就相当于type是text的input标签--%>
            <form:input id="ttl" path="title"/>
        </p>
        <p>
            <label for="atr">作者:</label>
            <form:input id="atr" path="author"/>
        </p>
        <p>
            <label for="isb">ISBN号:</label>
            <form:input id="isb" path="isbn"/>
        </p>
        <p>
            <input type="submit" value="修改">
        </p>
    </fieldset>
</form:form>
</body>
</html>

运行结果

先查看所有书籍,然后添加一本书,然后修改这本书的书名。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/shu15121856/article/details/80985772