javascript
Spring MVC-05循序渐进之数据绑定和form标签库(下) 实战从0到1
- 概述
- 功能概述
- 搭建SpringMVC Maven工程
- pom.xml
- 部署描述符web.xml
- 配置Spring MVC配置文件
- 日志配置文件
- Domain類
- Controller類
- Service類
- 視圖
- artisan_list測試
- artisan_add
- 編寫超鏈接標簽中對應的uri
- Controller映射方法
- AddArtisan.jsp
- 測試結果
- Edit Artisan
- 編寫uri
- 編寫映射方法
- 編寫EditArtisan.jsp
- update映射方法
- 測試
- 總結
- 源碼
概述
Spring MVC-05循序漸進之數據綁定和form標簽庫(上) 博文中我們學習了數據綁定和form標簽庫,那我們來寫一個小demo練習下吧。
功能概述
假設有個Artisan管理頁面,先拋開花里胡哨的前端,我們用最丑最簡單的方式實現,來體會下Spring MVC數據綁定及表單的操作過程 。如下圖
搭建SpringMVC Maven工程
pom.xml
添加Maven依賴,主要的依賴包是spring-webmvc-${version},這里我們采用4.3.9版本,同時使用JDK7來編譯
<project xmlns="http://maven.apache.org/POM/4.0.0" 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.artisan</groupId><artifactId>chapter05a</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>chapter05a Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.9.RELEASE</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency></dependencies><build><finalName>chapter05a</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.5.1</version><configuration><source>1.7</source><target>1.7</target><compilerArgument>-Xlint:all</compilerArgument><showWarnings>true</showWarnings><showDeprecation>true</showDeprecation></configuration></plugin></plugins></build> </project>部署描述符web.xml
配置DispatcherServlet,指定SpringMVC配置文件的路徑,同時為避免中文亂碼配置filter ,指定CharacterEncodingFilter為UTF-8。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"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"><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/config/springmvc-config.xml</param-value></init-param><load-on-startup>1</load-on-startup> </servlet><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>配置Spring MVC配置文件
通過context:component-scan 結合注解,掃描bean 。
同時配置靜態資源文件過濾,以及視圖解析器。
日志配置文件
先簡單配置下 ,啟動Spring容器的時候不報錯即可。
log4j.rootLogger=INFO,A1 log4j.logger.org.springframework=info log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%nDomain類
根據我們的構想及頁面原型,這個Demo中的domain類Artisan,應該有如下幾個屬性
private long id;private String name;private String code;private String sex;private Org org;有個類型為Org 的org屬性,其中 Org有如下2個屬性
private int orgId;private String orgName; package com.artisan.springmvc.domian;public class Artisan {private long id;private String name;private String code;private String sex;private Org org;/*** * 創建一個新的實例 Artisan.* * @param id* @param name* @param code* @param sex* @param org*/public Artisan(long id, String name, String code, String sex, Org org) {super();this.id = id;this.name = name;this.code = code;this.sex = sex;this.org = org;}/*** * 默認構造函數**/public Artisan() {super();}public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public Org getOrg() {return org;}public void setOrg(Org org) {this.org = org;}} package com.artisan.springmvc.domian;public class Org {private int orgId;private String orgName;/*** * 創建一個新的實例 Org. 默認的構造函數需要有,否則 org.apache.jasper.JasperException:* org.springframework.beans.NullValueInNestedPathException: Invalid* property 'org' of bean class [com.artisan.springmvc.domian.Artisan]:* Could not instantiate property type [com.artisan.springmvc.domian.Org] to* auto-grow nested property path; nested exception is* org.springframework.beans.BeanInstantiationException: Failed to* instantiate [com.artisan.springmvc.domian.Org]: Is it an abstract class?;* nested exception is java.lang.InstantiationException:* com.artisan.springmvc.domian.Org** */public Org() {super();}/*** * 創建一個新的實例 Org.* * @param orgId* @param orgName*/public Org(int orgId, String orgName) {super();this.orgId = orgId;this.orgName = orgName;}public int getOrgId() {return orgId;}public void setOrgId(int orgId) {this.orgId = orgId;}public String getOrgName() {return orgName;}public void setOrgName(String orgName) {this.orgName = orgName;}}Domain域的類,沒什么好說的,提供默認構造函數,和前臺一致即可。
Controller類
第一步,首先獲取一個Artisan列表, 個人習慣先開發Controller
按照設計輸入http://ip:port/context/artisan/artisanList 可獲取全部的Artisan數據
@Controller @RequestMapping("/artisan") public class ArtisanController {private static final Logger logger = Logger.getLogger(ArtisanController.class);private ArtisanService artisanService;public ArtisanService getArtisanService() {return artisanService;}/*** * @Title: setArtisanService * @Description: 通過 @Autowired注入ArtisanService* @param @param artisanService 參數 * @return void 返回類型 * @throws*/@Autowiredpublic void setArtisanService(ArtisanService artisanService) {this.artisanService = artisanService;}@RequestMapping(value="/artisanList",method=RequestMethod.GET)public String getAllArtisans(Model model){logger.info("getAllArtisans called....");List<Artisan> artisanList = artisanService.getArtisans();// 添加到Model中,以便前臺能訪問到model.addAttribute("artisanList", artisanList);return "ArtisanList";}}通過在類上標注注解@Controller ,配合component-scan掃描,使其成為一個控制器,然后標注了@RequestMapping(“/artisan”),在類層級上標注了請求路徑,這個控制器中所有的方法都基于/artisan。
通過@Autowired自動注入service,然后通過artisanService.getArtisans()獲取模擬的artisanList
緊接著將數據添加到Model中,以便前臺能訪問到 model.addAttribute(“artisanList”, artisanList);
最后返回了一個視圖ArtisanList,結合SpringMVC配置文件中的視圖解析器,會轉發到/WEB-INF/jsp/目錄下的ArtisanList.jsp
Service類
目前只有一個獲取全部數據的接口,后續根據功能逐個增加
package com.artisan.springmvc.service;import java.util.List;import com.artisan.springmvc.domian.Artisan;public interface ArtisanService {// 獲取所有的ArtisanList<Artisan> getArtisans(); }接口實現類
package com.artisan.springmvc.service;import java.util.ArrayList; import java.util.List;import org.springframework.stereotype.Service;import com.artisan.springmvc.domian.Artisan; import com.artisan.springmvc.domian.Org;/*** * @ClassName: ArtisanServiceImpl* @Description: 通過@Service標注的服務層 ,* @author Mr.Yang* @date 2018年1月30日**/@Service public class ArtisanServiceImpl implements ArtisanService {/** this implementation is not thread-safe*/List<Artisan> artisanList = null;String sex = null;/*** * 創建一個新的實例 ArtisanServiceImpl的同時初始化模擬數據**/public ArtisanServiceImpl() {super();// 初始化模式數據artisanList = new ArrayList<Artisan>();for (int i = 0; i < 10; i++) {if (i%2 == 0) {sex = "男";}else {sex="女";}artisanList.add(new Artisan(i, "Artisan" + i, "ATSCode" + i, sex, new Org(i, "org" + i)));}}@Overridepublic List<Artisan> getArtisans() {return artisanList;}}視圖
引入c標簽,然后對后臺Model中的artisanList進行遍歷顯示數據。 有CSS修飾樣式。
ArtisanList.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Artisan List</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body><div id="global"> <h1>Artisan List</h1><p><a href='<c:url value="/artisan/artisan_input"/>'>Add Artisan</a> </p><table border="1" cellspacing="0"> <tr><th align="center">OrgName</th><th align="center">ArtisanName</th><th align="center">Code</th><th align="center">Sex</th><th align="center" colspan="2">Operation</th> </tr><!-- var要循環集合的別名 --> <c:forEach items="${artisanList}" var="artisan" varStatus="status"><tr <c:if test="${status.count%2==0}">bgcolor="#7CCD7C"</c:if> align="center"><td>${artisan.org.orgName}</td><td>${artisan.name}</td><td>${artisan.code}</td><td>${artisan.sex}</td><td><a href>Edit</a></td></tr> </c:forEach> </table> </div> </body> </html>artisan_list測試
啟動tomcat,然后訪問
http://localhost:8080/chapter05a/artisan/artisanList 即可獲取全部的ArtisanList
artisan_add
我們來分析一下artisan_add的邏輯
1. 通過點擊ArtisanList.jsp頁面上的Add Artisan 超鏈接標簽,使用JSTL標記的URL解決路徑訪問的問題,跳轉到添加頁面
2. 再添加頁面中加載Org下拉列表,輸入信息后,提交觸發保存Artisan的操作
3. 后臺保存完成后 ,重定向到ArtisanList,展示數據。
編寫超鏈接標簽中對應的uri
<a href='<c:url value="/artisan/artisan_input"/>'>Add Artisan</a>使用JSTL標記的URL解決路徑訪問的問題, 因為我們在web.xml中配置攔截所有的請求,因此這個請求會被DispatcherServlet攔截,映射到如下的方法中
Controller映射方法
/*** * @Title: inputArtisan * @Description: 進入inputArtisan的頁面 * @param @return 參數 * @return String 返回類型 * @throws*/@RequestMapping(value="/artisan_input")public String inputArtisan(Model model){// 獲取全部的orgList<Org> orgs = artisanService.getAllOrgs();// 加載org到Model中以便前臺展示model.addAttribute("orgs", orgs);// 前臺form commandName為artisan,因此必須保證model中存在一個artisanmodel.addAttribute("artisan",new Artisan());return "AddArtisan";}因為添加頁面需要展示org列表,所以必須從后臺加載全部的org,放到model中,確保前臺頁面可以通過表達式獲取到對應的數據。 同時,前臺添加Artisan的form ,打算加入commandName屬性方便識別, 如下 form:form commandName="artisan" commandName 為artisan,如果該屬性存在,則必須在返回包含該表單的視圖的請求處理方法中添加對應的模型屬性.
返回的字符串 AddArtisan,SpringMVC會根據視圖解析器的配置規則
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean>映射到/WEB-INF/jsp/AddArtisan.jsp
AddArtisan.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Add Artisan Form</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body><div id="global"> <form:form commandName="artisan" action="artisan_add" method="post"><fieldset><legend>Add an Artisan</legend><p><label for="orgs">orgName: </label><form:select id="org" path="org.orgId" items="${orgs}" itemValue="orgId" itemLabel="orgName"/></p><p><label for="name">name: </label><form:input id="name" path="name"/></p><p><label for="code">code: </label><form:input id="code" path="code"/></p><p><label for="sex">sex: </label><form:input id="sex" path="sex"/></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Add Artisan"></p></fieldset> </form:form> </div> </body> </html>Org的下拉列表采用form的select標簽,點擊超鏈接跳轉頁面的方法中,調用后端的方法獲取全部的orgList,同時存放到model中,便于前端展示。 同時綁定了path=”org.orgId” ,后端提供根據頁面傳入的orgId獲取Org的接口及實現類
實現類如下
@Overridepublic Org getOrg(int orgId) {for (Org org:orgList) {if (orgId == org.getOrgId()) {return org;}}return null;}根據前端選擇orgId, 返回對應的org實體類。
然后設置給artisan, 最后調用服務層的方法保存artisan到list中,最后重定向到list列表
代碼如下
@RequestMapping(value="/artisan_add",method=RequestMethod.POST)public String addArtisan(@ModelAttribute Artisan artisan){logger.info("addArtisan called...");// 獲取頁面的數據logger.info("orgId:" + artisan.getOrg().getOrgId());logger.info("Name:" + artisan.getName());logger.info("Code:" + artisan.getCode());logger.info("Sex:" + artisan.getSex());//根據前臺傳入綁定的orgId,獲取OrgOrg org = artisanService.getOrg(artisan.getOrg().getOrgId());// 設置orgartisan.setOrg(org);// 保存artisanartisanService.addArtisan(artisan);// 跳轉到list頁面return "redirect:/artisan/artisan_list";}測試結果
Edit Artisan
下面我們來梳理一下編輯的邏輯
1. 點擊Edit按鈕,進入編輯頁面,這個頁面需要將對應的數據加載顯示,然后提供用戶編輯
2. 用戶點擊UPDATE按鈕后,提交到后端更新數據,然后重定向到list頁面
編寫uri
第一步展示list的時候,我們已經從后端加載了artisan的id ,所以編輯的時候根據artisan#id去編輯,這樣href如下
<a href="artisan_edit/${artisan.id}">Edit</a>編寫映射方法
根據artisan_edit/${artisan.id} 映射到如下方法
/*** * @Title: editArtisan * @Description: 跳轉到編輯Artisan頁面 * @param @param model* @param @param id* @param @return 參數 * @return String 返回類型 * @throws*/@RequestMapping(value="/artisan_edit/{id}")public String editArtisan(Model model,@PathVariable long id){logger.info("Artisan ID:" + id);// 加載Org全部數據 用于選擇List<Org> orgList = artisanService.getAllOrgs();// 添加到model,以便前臺訪問model.addAttribute("orgList", orgList);// 根據傳入的id,獲取對應的artisan信息 用于編輯頁面展示Artisan信息Artisan artisan = artisanService.getArtisanById(id);// 添加到model,以便前臺訪問model.addAttribute("artisan", artisan);return "EditArtisan";}編寫EditArtisan.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><% String path = request.getContextPath(); //獲得本項目的地址(例如: http://localhost:8080/domain/)賦值給basePath變量 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; // 將 "項目路徑basePath" 放入pageContext中,待以后用EL表達式讀出。 pageContext.setAttribute("basePath",basePath); %> <!DOCTYPE HTML> <html> <head> <title>Edit Artisan Form</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body><div id="global"> <form:form commandName="artisan" action="${pageScope.basePath}/artisan/artisan_update" method="post"><fieldset><legend>Edit Artisan</legend><form:hidden path="id"/><p><label for="orgs">orgName: </label><form:select id="org" path="org.orgId" items="${orgList}" itemValue="orgId" itemLabel="orgName"/></p><p><label for="name">name: </label><form:input id="name" path="name"/></p><p><label for="code">code: </label><form:input id="code" path="code"/></p><p><label for="sex">sex: </label><form:input id="sex" path="sex"/></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Update Artisan"></p></fieldset> </form:form> </div> </body> </html>update映射方法
點擊提交后,action=”${pageScope.basePath}/artisan/artisan_update” ,映射
@RequestMapping(value="/artisan_update",method=RequestMethod.POST)public String artisanUpdate(@ModelAttribute Artisan artisan){logger.info("artisanUpdate called");logger.info("artisan orgId:" + artisan.getOrg().getOrgId());logger.info("artisan Id:" + artisan.getId());logger.info("artisan Name:" + artisan.getName());logger.info("artisan Sex:" + artisan.getSex());logger.info("artisan Code:" + artisan.getCode());// 根據orgId獲取orgOrg org = artisanService.getOrg(artisan.getOrg().getOrgId());logger.info("Org Name :" + org.getOrgName());artisan.setOrg(org);// 更新數據artisanService.updateArtisan(artisan);return "redirect:/artisan/artisan_list";}測試
修改一條數據,如下
總結
至此,一個簡單的實例已經編寫完畢,重點是體會思路及spring mvc 及form的應用。
源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
總結
以上是生活随笔為你收集整理的Spring MVC-05循序渐进之数据绑定和form标签库(下) 实战从0到1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring MVC-06循序渐进之Co
- 下一篇: Spring MVC-07循序渐进之验证