第2章 Spring MVC基礎:4、表單標籤庫與資料繫結
學習目標:
表單標籤庫與資料繫結
學習大綱:
一、表單標籤庫
二、資料繫結
學習內容:
資料繫結是將使用者引數輸入值繫結到領域模型的一種特性。在Spring MVC的Controller和View引數資料傳遞中,所有的HTTP請求的型別均為字串。如果模型需要繫結的型別為double或int,則需要手動進行型別轉換。而有了資料繫結後,就無需再手動將HTTP請求中的Spring型別轉化為模型需要的型別。資料繫結的另一個好處是,當輸入驗證失敗時,會重新生成一個HTML表單,無須重新填寫輸入欄位。在Spring MVC中,為了方便、高效的使用資料繫結,還需要學習表單標籤庫。
“資料繫結”是前端框架技術中的一個概念,比如微信小程式開發中的資料繫結、vue.js中的雙向資料繫結。
我們知道前端框架的意義是讓前端開發更方便、更高效,那麼資料繫結肯定也是為這個目的服務的。
資料繫結是為了實現一種“動態”的效果,後臺的資料更新了,前端頁面也自動更新;前端頁面上的資料更新了,後臺的資料也自動更新。
一、表單標籤庫
表單標籤庫中包含了可以用在JSP頁面中渲染HTML元素的標籤。JSP頁面使用Spring表單標籤庫時,必須在JSP頁面開頭處宣告taglib指令,指令程式碼如下:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
表單標籤庫中有form
、input
、password
、hidden
、textarea
、checkbox
、checkboxes
、radiobutton
、radiobuttons
、select
、option
、options
、errors
。
1.表單標籤
表單標籤,語法格式如下:
<form:form modelAttribute="xxx" method="post" action="xxx">
……
</form:form>
除了具有HTML表單元素屬性外,表單標籤還有具有acceptCharset、commandName、cssClass、cssStyle、htmlEscape和modelAttribute
最常用的是modelAttribute
屬性:用來暴露form backing object的模型屬性名稱。預設為command。
其中,commandName
和modelAttribute
屬性功能基本一致,屬性值繫結一個JavaBean物件。
2.input標籤
input標籤,語法格式如下:
<form:input path="xxx"/>
該標籤除了cssClass、cssStyle、htmlEscape
屬性外,還有一個最重要的屬性path
。path
屬性將文字框輸入值繫結到form backing object的一個屬性。
3.password標籤
password標籤,語法格式如下:
<form:password path="xxx"/>
該標籤與input標籤用法完全一致,不再贅述。
4.hidden標籤
hidden標籤,語法格式如下:
<form:hidden path="xxx"/>
該標籤與input標籤用法基本一致,只不過它不可顯示,不支援cssClass和cssStyle屬性。
5.textarea標籤
textarea基本上就是一個支援多行輸入的input元素,語法格式如下:
<form:textarea path="xxx"/>
該標籤與input標籤用法完全一致,不再贅述。
6.checkbox標籤
checkbox標籤,語法格式如下:
<form:checkbox path="xxx" value="xxx"/>
多個path相同的checkbox標籤,它們是一個選項組,允許多選。選項值繫結到一個數組屬性。示例程式碼如下:
<form:checkbox path="friends" value="張三"/>張三
<form:checkbox path="friends" value="李四"/>李四
<form:checkbox path="friends" value="王五"/>王五
上述示例程式碼中的複選框的值繫結到一個字串陣列屬性friends(String[ ] friends)。
7.checkboxes標籤
checkboxes
標籤渲染多個複選框,是一個選項組,等價於多個path相同的checkbox標籤。它有3個非常重要的屬性:items、itemLabel和itemValue
。
items
:用於生成input元素的Collection、Map或Array。
itemLabel
:items屬性中指定的集合物件的屬性,為每個input元素提供label。
itemValue
:items屬性中指定的集合物件的屬性,為每個input元素提供value。
checkboxes標籤語法格式如下:
<form:checkboxes items="xxx" path="xxx"/>
示例程式碼如下:
<form:checkboxes items="${hobbys}" path="hobby"/>
上述程式碼是將model屬性hobbys的內容(集合元素)渲染為複選框。
8.radiobutton標籤
radiobutton
標籤,語法格式如下:
<form:radiobutton path="xxx" value="xxx"/>
多個path相同的radiobutton
標籤,它們是一個選項組,只允許單選。
9.radiobuttons標籤
radiobuttons
標籤渲染多個radio,是一個選項組,等價於多個path相同的radiobutton標籤。radiobuttons標籤,語法格式如下:
<form:radiobuttons path="xxx" items="xxx"/>
該標籤的itemLabel和itemValue屬性與checkboxes標籤的itemLabel和itemValue屬性完全一樣,但只允許單選。
10.select標籤
select標籤的選項可能來自其屬性items指定的集合,或者來自一個巢狀的option
標籤或options
標籤。語法格式如下:
<`form:select path="xxx" items="xxx" />`
或
<form:select path="xxx" items="xxx" >
<option value="xxx">xxx</option>
</ form:select>
或
<form:select path="xxx">
<form:options items="xxx"/>
</form:select>
該標籤的itemLabel和itemValue
屬性與checkboxes
標籤的itemLabel
和itemValue
屬性完全一樣。
11.options標籤
options標籤生成一個select標籤的選項列表。因此,需要與select標籤一同使用,具體用法參見select標籤。
12.errors標籤
errors標籤渲染一個或者多個span元素,每個span元素包含一個錯誤訊息。它可以用於顯示一個特定的錯誤訊息,也可以顯示所有錯誤訊息。語法如下:
<form:errors path="*"/>
或
<form:errors path="xxx"/>
其中,“*”表示顯示所有錯誤訊息;“xxx”表示顯示由“xxx”指定的特定錯誤訊息。
二、資料繫結
為了讓讀者進一步學習資料繫結和表單標籤,本節給出了一個應用例項ch2_4。ch2_4應用中實現了User類屬性和JSP頁面中表單引數的繫結,同時在JSP頁面中分別展示了input、textarea、checkbox、checkboxs、select
等標籤。
【例2-4】資料繫結和表單標籤。
具體實現步驟如下:
1、建立應用並匯入相關的JAR包
在ch2_4應用中需要使用JSTL,因此不僅需要將Spring MVC相關jar包複製到應用的WEB-INF/lib目錄下還需要從Tomcat的webapps\examples\WEB-INF\lib目錄下將JSTL相關jar包複製到應用的WEB-INF/lib目錄下。
2、建立Web和Spring MVC配置類
在ch2_4應用的src目錄下建立名為config的包,並在該包中建立Web配置類WebConfig
和Spring MVC配置類SpringMVCConfig
。
為了避免中文亂碼問題,需要在Web配置類WebConfig
中註冊編碼過濾器,同時JSP頁面編碼設定為UTF-8,form表單的提交方式為post。
WebConfig的程式碼如下:
package config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
public class WebConfig implements WebApplicationInitializer{
@Override
public void onStartup(ServletContext arg0) throws ServletException {
AnnotationConfigWebApplicationContext ctx
= new AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfig.class);//註冊Spring MVC的Java配置類SpringMVCConfig
ctx.setServletContext(arg0);//和當前ServletContext關聯
/**
* 註冊Spring MVC的DispatcherServlet
*/
javax.servlet.ServletRegistration.Dynamic servlet =
arg0.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
/**
* 註冊字元編碼過濾器
*/
javax.servlet.FilterRegistration.Dynamic filter =
arg0.addFilter("characterEncodingFilter", CharacterEncodingFilter.class);
filter.setInitParameter("encoding", "UTF-8");
filter.addMappingForUrlPatterns(null, false, "/*");
}
}
SpringMVCConfig的程式碼如下:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"controller","service"})
public class SpringMVCConfig implements WebMvcConfigurer {
/**
* 配置檢視解析器
*/
@Bean
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/**
* 配置靜態資源
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
//addResourceHandler指的是對外暴露的訪問路徑
//addResourceLocations指的是靜態資源存放的位置
}
}
3、建立View層
View層包含兩個JSP頁面,一個是資訊輸入頁面userAdd.jsp,一個是資訊顯示頁面userList.jsp。在ch2_4應用的WEB-INF/jsp/目錄下,建立這兩個JSP頁面。
在userAdd.jsp頁面中將Map型別的hobbys繫結到checkboxes上,將String[ ]型別的carrers和houseRegisters繫結到select上,實現通過option標籤對select新增選項,同時表單的method方法需指定為post來避免中文亂碼問題。
在userList.jsp頁面中使用JSTL標籤遍歷集合中的使用者資訊。
userAdd.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>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form:form modelAttribute="user" method="post" action="${pageContext.request.contextPath }/user/save">
<fieldset>
<legend>新增一個使用者</legend>
<p>
<label>使用者名稱:</label>
<form:input path="userName"/>
</p>
<p>
<label>愛好:</label>
<form:checkboxes items="${hobbys}" path="hobby" />
</p>
<p>
<label>朋友:</label>
<form:checkbox path="friends" value="張三"/>張三
<form:checkbox path="friends" value="李四"/>李四
<form:checkbox path="friends" value="王五"/>王五
<form:checkbox path="friends" value="趙六"/>趙六
</p>
<p>
<label>職業:</label>
<form:select path="carrer">
<option/>請選擇職業
<form:options items="${carrers }"/>
</form:select>
</p>
<p>
<label>戶籍:</label>
<form:select path="houseRegister">
<option/>請選擇戶籍
<form:options items="${houseRegisters }"/>
</form:select>
</p>
<p>
<label>個人描述:</label>
<form:textarea path="remark" rows="5"/>
</p>
<p id="buttons">
<input id="reset" type="reset">
<input id="submit" type="submit" value="新增">
</p>
</fieldset>
</form:form>
</body>
</html>
userList.jsp的程式碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>使用者列表</h1>
<a href="<c:url value="/user/input"/>">繼續新增</a>
<table>
<tr>
<th>使用者名稱</th>
<th>興趣愛好</th>
<th>朋友</th>
<th>職業</th>
<th>戶籍</th>
<th>個人描述</th>
</tr>
<!-- JSTL標籤,請參考本書的相關內容 -->
<c:forEach items="${users}" var="user">
<tr>
<td>${user.userName }</td>
<td>
<c:forEach items="${user.hobby }" var="hobby">
${hobby }
</c:forEach>
</td>
<td>
<c:forEach items="${user.friends }" var="friend">
${friend }
</c:forEach>
</td>
<td>${user.carrer }</td>
<td>${user.houseRegister }</td>
<td>${user.remark }</td>
</tr>
</c:forEach>
</table>
</body>
</html>
4、建立領域模型
在ch2_4應用中實現了User類屬性和JSP頁面中表單引數的繫結,User類包含和表單引數名對應的屬性,以及屬性的set和get方法。在ch2_4應用的src目錄下,建立pojo包,並在該包中建立User類。
User類的程式碼如下:
package pojo;
public class User {
private String userName;
private String[] hobby;//興趣愛好
private String[] friends;//朋友
private String carrer;
private String houseRegister;
private String remark;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String[] getFriends() {
return friends;
}
public void setFriends(String[] friends) {
this.friends = friends;
}
public String getCarrer() {
return carrer;
}
public void setCarrer(String carrer) {
this.carrer = carrer;
}
public String getHouseRegister() {
return houseRegister;
}
public void setHouseRegister(String houseRegister) {
this.houseRegister = houseRegister;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
5、建立Service層
在ch2_4應用中使用了Service層,在Service層使用靜態集合變數users模擬資料庫儲存使用者資訊,包括新增使用者和查詢使用者兩個功能。在ch2_4應用的src目錄下,建立Service包,並在該包中建立UserService介面和UserServiceImpl實現類。
UserService介面的程式碼如下:
package service;
import java.util.ArrayList;
import pojo.User;
public interface UserService {
boolean addUser(User u);
ArrayList<User> getUsers();
}
UserServiceImpl實現類的程式碼如下:
package service;
import java.util.ArrayList;
import org.springframework.stereotype.Service;
import pojo.User;
@Service
public class UserServiceImpl implements UserService{
//使用靜態集合變數users模擬資料庫
private static ArrayList<User> users = new ArrayList<User>();
@Override
public boolean addUser(User u) {
if(!"IT民工".equals(u.getCarrer())){//不允許新增IT民工
users.add(u);
return true;
}
return false;
}
@Override
public ArrayList<User> getUsers() {
return users;
}
}
6、建立Controller層
在Controller類的UserController中定義了請求處理方法,包括處理user/input請求的inputUser方法和處理user/save請求的addUser方法,其中在addUser方法中用到了重定向。在UserController類中,通過@Autowired註解在UserController物件中主動注入UserController物件,實現對user物件的新增和查詢操作;通過model的addAttribute方法將User類物件、HashMap型別的hobbys物件、String[ ]型別的carrers物件以及String[ ]型別的houseRegisters物件傳遞給View(userAdd.jsp)。
在ch2_4應用的src目錄下,建立controller包,並在該包中建立UserController控制器類。
UserController類的程式碼如下:
package controller;
import java.util.HashMap;
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.RequestMapping;
import pojo.User;
import service.UserService;
@Controller
@RequestMapping("/user")
public class UserController {
// 得到一個用來記錄日誌的物件,這樣列印資訊的時候能夠標記列印的是那個類的資訊
private static final Log logger = LogFactory.getLog(UserController.class);
@Autowired
private UserService userService;
@RequestMapping(value = "/input")
public String inputUser(Model model) {
HashMap<String, String> hobbys = new HashMap<String, String>();
hobbys.put("籃球", "籃球");
hobbys.put("乒乓球", "乒乓球");
hobbys.put("電玩", "電玩");
hobbys.put("游泳", "游泳");
// 如果model中沒有user屬性,userAdd.jsp會丟擲異常,因為表單標籤無法找到
// modelAttribute屬性指定的form backing object
model.addAttribute("user", new User());
model.addAttribute("hobbys", hobbys);
model.addAttribute("carrers", new String[] { "教師", "學生", "coding搬運工", "IT民工", "其它" });
model.addAttribute("houseRegisters", new String[] { "北京", "上海", "廣州", "深圳", "其它" });
return "userAdd";
}
@RequestMapping(value = "/save")
public String addUser(@ModelAttribute User user, Model model) {
if (userService.addUser(user)) {
logger.info("成功");
return "redirect:/user/list";
} else {
logger.info("失敗");
HashMap<String, String> hobbys = new HashMap<String, String>();
hobbys.put("籃球", "籃球");
hobbys.put("乒乓球", "乒乓球");
hobbys.put("電玩", "電玩");
hobbys.put("游泳", "游泳");
// 這裡不需要model.addAttribute("user", new
// User()),因為@ModelAttribute指定form backing object
model.addAttribute("hobbys", hobbys);
model.addAttribute("carrers", new String[] { "教師", "學生", "coding搬運工", "IT民工", "其它" });
model.addAttribute("houseRegisters", new String[] { "北京", "上海", "廣州", "深圳", "其它" });
return "userAdd";
}
}
@RequestMapping(value = "/list")
public String listUsers(Model model) {
List<User> users = userService.getUsers();
model.addAttribute("users", users);
return "userList";
}
}
7、測試應用
通過以下地址訪問應用:
新增成功後,重定向到資訊顯示頁面。
學習時間:
學習產出:
1、 技術筆記 1 遍
2、CSDN 技術部落格 1 篇