1、控制器接收各種請求參數javascript
代碼測試環境:html
接收各種參數的控制器--ParamsControllerjava
package com.ssm.chapter15.controller; @Controller @RequestMapping("/params") public class ParamsController { // 各類控制器方法 }
先看一下目錄結構:mysql
這裏須要知道的知識點是,WebContent文件夾下的.jsp文件均可以經過http://localhost:8080/工程名/文件名.jsp直接訪問。jquery
而WEB-INF裏面的文件,必須經過Spring MVC 中的Controller控制器產生映射才能訪問。web
1.接收普通請求參數spring
params.jsp文件的內容以下,其中action="./params/commonParams.do"表示提交按鈕按下後,跳轉到action指定的頁面。sql
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!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> <body> <!-- 根據你的須要改變請求url --> <form id="form" action="./params/commonParams.do"> <table> <tr> <td>角色名稱</td> <td><input id="roleName" name="roleName" value="" /></td> </tr> <tr> <td>備註</td> <td><input id="note" name="note" /></td> </tr> <tr> <td></td> <td align="right"><input type="submit" value="提交" /></td> </tr> </table> </form> </body> </html>
而對應的控制器方法:commonParams方法數據庫
@Controller @RequestMapping("/params") public class ParamsController { @RequestMapping("/commonParams") public ModelAndView commonParams(String roleName, String note) { System.out.println("roleName =>" + roleName); System.out.println("note =>" + note); ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; }
這裏由於當前Spring MVC 比較智能化,若是傳遞進來的參數名稱和HTTP的保存一致,意思就是傳遞進來的參數名稱爲roleName和note,而params.jsp中<input id="roleName" name="roleName" value="" />和<input id="note" name="note" />兩個參數名稱都和roleName和note一致,所以,能夠獲取到params.js中提交的參數。apache
測試:首先輸入訪問表單,輸入任意參數,並提交
而後,正確跳轉到./params/commonParams.do?roleName=mingcheng¬e=beizhu,這一URL,說明參數傳遞成功。
可是,在參數不少的狀況下,再使用這樣的方式,顯然所寫方法的參數就會很是多,這是應該考慮到使用一個POJO來管理這些參數。在沒有任何註解的狀況下,Spring MVC 也有映射POJO的能力。
新建一個角色參數類,將兩個參數封裝到類中:
package com.ssm.chapter14.pojo; public class RoleParams { private String roleName; private String note;
/*getter and setter*/ }
而後增長控制器方法:因爲上面的POJO的屬性和HTTP參數(jsp文件中的參數)一一對應了,而後在commonParamPojo方法中將POJO類對象RoleParams roleParams當成方法的參數,也能夠在沒有任何註解的狀況下實現參數的有效傳遞。
@RequestMapping("/commonParamPojo") public ModelAndView commonParamPojo(RoleParams roleParams) { System.out.println("roleName =>" + roleParams.getRoleName()); System.out.println("note =>" + roleParams.getNote()); ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; }
另外,還須要修改jsp中的action成<form id="form" action="./params/commonParamPojo.do">才能進行測試。
2.使用@RequestParam註解獲取參數
前面的兩種狀況,僅僅在參數名稱和jsp文件中的參數名稱一一對應時纔有效。那麼,若是修改jsp中的參數名稱,例如,<td><input id="role_name" name="role_name" value="" /></td>將roleName修改爲role_Name,此時因爲參數不一致,就沒法再進行自動對應傳遞了。
能夠用@RequestParam註解獲取參數的方式解決這個問題:使用@RequestParam("role_name")來說HTTP的參數名稱(即jsp文件中的參數名稱)和傳遞進去的roleName參數一一對應。
@RequestMapping("/requestParam") //使用@RequestParam("role_name")指定映射HTTP參數名稱 public ModelAndView requestParam(@RequestParam("role_name") String roleName, String note) { System.out.println("roleName =>" + roleName); System.out.println("note =>" + note); ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; }
一樣,修改action,而後也能夠獲得正確的測試結果:
3.使用URL傳遞參數
一些網站使用URL的形式傳遞參數,對於一些業務比較簡單的應用是很是常見的,若是想把得到數據庫中id爲1的role的信息,那麼就寫成/params/getRole/1,這裏的1就表明角色編號,只不過是在URL中傳遞。Spring MVC 也提供了支持。
須要經過@RequestMapping註解和@PathVariable註解協做完成。其中,
@RequestMapping("/getRole/{id}")中的{id}表示處理器須要接受一個由URL組成的參數,且參數名稱爲id
@PathVariable("id")的意思是獲取定義在@RequestMapping中參數名稱爲id的參數,這樣就能夠在方法內獲取這個參數了
而後經過劫色服務類獲取角色對象,並將其綁定到視圖中,將視圖設置爲JSON視圖。
//注入角色服務對象 @Autowired RoleService roleService; //{id}表明接收一個參數 @RequestMapping("/getRole/{id}") //註解@PathVariable表示從URL的請求地址中獲取參數 public ModelAndView pathVariable(@PathVariable("id") Long id) { Role role = roleService.getRole(id); ModelAndView mv = new ModelAndView(); //綁定數據模型 mv.addObject(role); //設置爲JSON視圖 mv.setView(new MappingJackson2JsonView()); return mv; }
測試結果:
4.傳遞JSON參數
假如要傳遞更多的參數。例如,對於查詢參數,假設還有開始行start和限制返回大小的limit,那麼加上roleName和note,就有了4個參數。
首先定義分頁參數POJO類PageParams類:
package com.ssm.chapter14.pojo; public class PageParams { private int start; private int limit;
/*getter and setter*/ }
而後,在原來的RoleParams類中增長一個PageParams類的對象,即:
package com.ssm.chapter14.pojo; public class RoleParams { private String roleName; private String note; private PageParams pageParams = null;// 分頁參數
/*getter and setter*/
}
這樣,查詢參數和分頁參數就均可以被傳遞了。這時,爲了模擬傳遞過程,在params.jsp中增長JavaScript腳本代碼:
/** 傳遞JSON**/ $(document).ready(function() { //JSON參數和類RoleParams一一對應 var data = { //角色查詢參數 roleName : 'role', note : 'note', //分頁參數 pageParams : { start : 0, limit : 4 } } //Jquery的post請求 $.post({ url : "./params/findRoles.do", //此處須要告知傳遞參數類型爲JSON,不能缺乏 contentType : "application/json", //將JSON轉化爲字符串傳遞 data : JSON.stringify(data), //成功後的方法 success : function(result) { } }); });
與之對應的findRoles方法:首先傳遞的JSON數據須要和對應參數的POJO保持一致。其次,在請求的時候須要告知請求的參數類型爲JSON。最後,傳遞的參數是一個字符串,而不是一個JSON,因此須要將JSON轉換成字符串。而後,經過@RequestBody註解,就能夠將和JavaScript代碼中對應的POJO類對象roleParams傳遞進去。
@RequestMapping("/findRoles") public ModelAndView findRoles(@RequestBody RoleParams roleParams) { List<Role> roleList = roleService.findRoles(roleParams); ModelAndView mv = new ModelAndView(); //綁定模型 //mv.addObject(roleList); //設置爲JSON視圖 //mv.setView(new MappingJackson2JsonView()); return mv; }
與之對應的Mapper中的配置:
<select id="findRoles" parameterType="com.ssm.chapter15.pojo.RoleParams" resultType="com.ssm.chapter14.pojo.Role"> select id, role_name as roleName, note from t_role <where> <if test="roleName != null"> and role_name like concat('%', #{roleName}, '%') </if> <if test="note != null"> and note like concat('%', #{note}, '%') </if> </where> limit #{pageParams.start}, #{pageParams.limit} </select>
另外,還須要將原來的params.jsp文件中的action配置刪除才能進行正確的測試:
5.接收列表數據和表單序列化
(1)傳遞數組給控制器,進行一次性刪除多個角色的操做
查看刪除前的數據庫:
mysql> select * from t_role; +----+-------------+--------+ | id | role_name | note | +----+-------------+--------+ | 1 | role_name_1 | note_1 | | 2 | role_name_2 | note_2 | | 3 | role_name_3 | note_3 | | 4 | role_name_4 | note_4 | | 5 | role_name_5 | note_5 | +----+-------------+--------+ 5 rows in set (0.00 sec)
jsp中對應的應該添加的JavaScript代碼:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!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> <!-- 加載Query文件--> <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.js"></script> <script type="text/javascript"> /**傳遞數組**/ $(document).ready(function() { //刪除角色數組 var idList = [ 1, 2, 3 ]; //jQuery的post請求 $.post({ url : "./params/deleteRoles.do", //將JSON轉化爲字符串傳遞 data : JSON.stringify(idList), //指定傳遞數據類型,不可缺乏 contentType : "application/json", //成功後的方法 success : function(result) { } }); }); </script> </head> </html>
與之對應的deleteRoles方法:這裏的@RequestBody List<Long> idList表示要求Spring MVC 將傳遞過來的JSON數組數據,轉換爲對應的Java集合類型。
@RequestMapping("/deleteRoles") public ModelAndView deleteRoles(@RequestBody List<Long> idList) { ModelAndView mv = new ModelAndView(); //刪除角色 int total = roleService.deleteRoles(idList); System.out.println(total); //綁定視圖 // mv.addObject("total", total); //JSON視圖 // mv.setView(new MappingJackson2JsonView()); return mv; }
roleService的deleteRoles方法的實現:
@Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public int deleteRoles(List<Long> idList) { int count = 0; for (Long id : idList) { count += roleDao.deleteRole(id); } return count; }
對應映射器Mapper中deleteRoles的配置:
<delete id="deleteRole" parameterType="long"> delete from t_role where id = #{id} </delete>
執行http://localhost:8080/Chapter14/deleteRoles.jsp後,控制檯輸出:3,而且數據庫的結果爲:
mysql> select * from t_role; +----+-------------+--------+ | id | role_name | note | +----+-------------+--------+ | 4 | role_name_4 | note_4 | | 5 | role_name_5 | note_5 | +----+-------------+--------+ 2 rows in set (0.00 sec)
(2)新增多個角色
同理addRole.jsp的內容是:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!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> <!-- 加載Query文件--> <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.js"></script> <script type="text/javascript"> $(document).ready(function () { //新增角色數組 var roleList = [ {roleName: 'role_name_1', note: 'note_1'}, {roleName: 'role_name_2', note: 'note_2'}, {roleName: 'role_name_3', note: 'note_3'} ]; //jQuery的post請求 $.post({ url: "./params/addRoles.do", //將JSON轉化爲字符串傳遞 data: JSON.stringify(roleList), contentType: "application/json", //成功後的方法 success: function (result) { } }); }); </script> </head> </html>
與之對應的視圖處理器中的addRoles方法:經過@RequestBody註解來獲取對應的角色列表參數,這樣就能夠在控制器中經過@ResponseBody將對應的JSON數據轉換成對象列表。
@RequestMapping("/addRoles") public ModelAndView addRoles(@RequestBody List<Role> roleList) { ModelAndView mv = new ModelAndView(); // 新增 int total = roleService.insertRoles(roleList); System.out.println(total); //綁定視圖 // mv.addObject("total", total); //JSON視圖 // mv.setView(new MappingJackson2JsonView()); return mv; }
roleService的insertRoles方法:
@Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public int insertRoles(List<Role> roleList) { int count = 0; for (Role role : roleList) { count += roleDao.insertRole(role); } return count; }
MyBatis映射器Mapper中的insertRoles配置:
<insert id="insertRole" parameterType="com.ssm.chapter14.pojo.Role" keyProperty="id" useGeneratedKeys="true"> insert into t_role (role_name, note) value(#{roleName}, #{note}) </insert>
在瀏覽器中輸入http://localhost:8080/Chapter14/addRoles.jsp而後查看數據庫的結果爲:
mysql> select * from t_role; +----+-------------+--------+ | id | role_name | note | +----+-------------+--------+ | 4 | role_name_4 | note_4 | | 5 | role_name_5 | note_5 | | 6 | role_name_1 | note_1 | | 7 | role_name_2 | note_2 | | 8 | role_name_3 | note_3 | +----+-------------+--------+ 5 rows in set (0.00 sec)
(3)經過表單序列化也能夠將表單數據轉換爲字符串傳遞到後臺,由於一些隱藏表單須要必定的計算,因此咱們也須要在用戶點擊提交按鈕後,經過序列化去提交表單。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>參數</title> <!-- 加載Query文件--> <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.js"> </script> <script type="text/javascript"> $(document).ready(function() { $("#commit").click(function() { var str = $("form").serialize(); //提交表單 $.post({ url : "./params/commonParamPojo2.do", //將form數據序列化,傳遞給後臺,則將數據以roleName=xxx&¬e=xxx傳遞 data : $("form").serialize(), //成功後的方法 success : function(result) { } }); }); }); </script> </head> <body> <form id="form"> <table> <tr> <td>角色名稱</td> <td><input id="roleName" name="roleName" value="" /></td> </tr> <tr> <td>備註</td> <td><input id="note" name="note" /></td> </tr> <tr> <td></td> <td align="right"><input id="commit" type="button" value="提交" /></td> </tr> </table> </form> </body> </html>
對應的commonParamPojo2方法:從表單獲取數據後,點擊提交按鈕,就會把數據在後臺打印出來。
@RequestMapping("/commonParamPojo2") public ModelAndView commonParamPojo2(String roleName, String note) { System.out.println("roleName =>" + roleName); System.out.println("note =>" + note); ModelAndView mv = new ModelAndView(); // mv.setViewName("index"); return mv; }
這裏須要說明的是,jquery中的$.post方法中,在執行完方法後,是沒法跳轉到Spring MVC 返回的ModelAndView類型的mv頁面的。具體能夠了解Ajax的內容。
2、重定向
經過以前的例子,咱們知道,能夠showRoleJsonInfo方法能夠接收三個參數,而後就能夠將這些參數轉化爲視圖,經過JSON的形式展現在頁面上。
@RequestMapping("/showRoleJsonInfo") public ModelAndView showRoleJsonInfo(Long id, String roleName, String note) { ModelAndView mv = new ModelAndView(); mv.setView(new MappingJackson2JsonView()); mv.addObject("id", id); mv.addObject("roleName", roleName); mv.addObject("note", note); return mv; }
例如:瀏覽器中輸入參數,就能夠將參數信息轉化成視圖,而後展現出來。
可是,若是想要實現:每當新增一一個角色信息時,須要其將數據以JSON視圖的形式展現給請求者。
實現方法是:在數據保存到數據庫後,由數據庫返回角色編號,再將角色信息傳遞showRoleJsonInfo方法,就能夠展現JSON視圖給請求者了。
1.在視圖控制器中新增重定向功能的方法:這裏須要注意的是,在執行完roleService.insertRole(role);語句後,插入數據庫的id會回填到role對象中。
而後經過返回字符串類型的"redirect:./showRoleJsonInfo.do"來進行重定向,其中若是字符串中帶有「redirect」,那麼就會認爲是一個重定向。
@RequestMapping("/addRole") //Model爲重定向數據模型,Spring MVC會自動初始化它 public String addRole(Model model, String roleName, String note) { Role role = new Role(); role.setRoleName(roleName); role.setNote(note); //插入角色後,會回填角色編號 roleService.insertRole(role); //綁定重定向數據模型 model.addAttribute("roleName", roleName); model.addAttribute("note", note); model.addAttribute("id", role.getId()); return "redirect:./showRoleJsonInfo.do"; }
只須要指定roleName和note參數便可,
2.不只能夠經過字符串來實現重定向,還能夠經過返回視圖來實現重定向
@RequestMapping("/addRole2") //ModelAndView對象Spring MVC會自定初始化它 public ModelAndView addRole2(ModelAndView mv, String roleName, String note) { Role role = new Role(); role.setRoleName(roleName); role.setNote(note); //插入角色後,會回填角色編號 roleService.insertRole(role); //綁定重定向數據模型 mv.addObject("roleName", roleName); mv.addObject("note", note); mv.addObject("id", role.getId()); mv.setViewName("redirect:./showRoleJsonInfo.do"); return mv; }
3.上面的例子都是傳遞簡單的String類型的參數,有些時候須要傳遞角色POJO,而不是一個個字段的傳遞。
修改showRoleJsonInfo方法成showRoleJsonInfo2,能夠以JSON的形式展現role對象
@RequestMapping("/showRoleJsonInfo2") public ModelAndView showRoleJsonInfo(Role role) { ModelAndView mv = new ModelAndView(); mv.setView(new MappingJackson2JsonView()); mv.addObject("role", role); return mv; }
可是,在URL重定向的過程當中,並不能有效傳遞對象,由於HTTP的重定向參數是以字符串傳遞的。爲了解決這個問題,可使用Spring MVC 的flash屬性,即RedirectAttributes參數,
@RequestMapping("/addRole3") //RedirectAttribute對象Spring MVC會自動初始化它 public String addRole3(RedirectAttributes ra, Role role) { //插入角色後,會回填角色編號 roleService.insertRole(role); //綁定重定向數據模型 ra.addFlashAttribute("role", role); return "redirect:./showRoleJsonInfo2.do"; }
這樣就能傳遞POJO對象到下一個地址了,Spring MVC 的實現方式是:將數據保存在Session中,重定向後就會將其消除,這樣就能傳遞數據給下一個地址了。
3、保存並獲取屬性參數(request、session、cookie和HTTP header)
Spring MVC 能夠經過一些註解從HTTP的request對象或者Session對象中獲取數據。
1.@RequestAttribute註解能夠從HTTP的request對象中取出請求屬性,只是範圍是在一次請求中存在。
request.jsp的內容以下,
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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>Insert title here</title> </head> <body> <% //設置請求屬性 request.setAttribute("id", 11L); //轉發給控制器 request.getRequestDispatcher("./attribute/requestAttribute.do").forward(request, response); %> </body> </html>
對應的控制器中的sessionAttribute方法:jsp文件中設置request的屬性id爲1,而後進行了轉發控制器,這樣將有對應的控制器去處理業務邏輯,而後由AttributeController控制器去處理它,經過(@RequestAttribute("id") Long id)將jsp中設置的request屬性獲取到。
@RequestAttribute和@RequestParam註解同樣,默認是不能爲空的,不然系統會拋出異常。可是,它們都有一個required配置項,只要配置成false,參數就能夠爲空了。
package com.ssm.chapter14.controller; @Controller @RequestMapping("/attribute") public class AttributeController { @Autowired private RoleService roleService = null; @RequestMapping("/requestAttribute") public ModelAndView reqAttr(@RequestAttribute("id") Long id) { ModelAndView mv = new ModelAndView(); Role role = roleService.getRole(id); mv.addObject("role", role); mv.setView(new MappingJackson2JsonView()); return mv; }
}
在瀏覽器中輸入http://localhost:8080/Chapter14/request.jsp,就能夠獲取jsp中定義的request屬性值,並跳轉到指定的界面。
2.@SessionAttribute和@SessionAttributes
這兩個註解都和HTTP的會話對象有關,在瀏覽器和服務器保持聯繫的時候HTTP會建立一個會話對象,這樣可讓咱們在和服務器會話期間經過它讀/寫會話對象的屬性,緩存必定的數據信息。
(1)經過@SessionAttributes設置會話屬性
@SessionAttributes註解只能對類進行標註,不能對方法或者參數註解。它能夠配置屬性名稱或者屬性類型。它的做用是當這個類被註解後,Spring MVC 執行完控制器的邏輯後,將數據模型中對應的屬性名稱或者屬性類型保存到HTTP的Session對象中。
sessionAttrs方法中,經過首先根據傳遞進來的id,經過查詢獲得role對象,而因爲AttributeController類中經過@SessionAttributes設置了名稱和類型,所以,id和role對象都會保存到Session對象中。
(2)經過@SessionAttribute獲取會話屬性
當瀏覽器中輸入/attribute/sessionAttributes.do?id=1時,id和role就會被保存到Session對象中,能夠在sessionAttribute.jsp中經過session.getAttribute("role")和session.getAttribute("id")兩個方法獲取到以前保存進去的role和id。
package com.ssm.chapter14.controller; @Controller @RequestMapping("/attribute") // 能夠配置數據模型的名稱和類型,二者取或關係 @SessionAttributes(names ={"id"}, types = { Role.class }) public class AttributeController { @Autowired private RoleService roleService = null; @RequestMapping("/sessionAttributes") public ModelAndView sessionAttrs(Long id) { ModelAndView mv = new ModelAndView(); Role role = roleService.getRole(id); //根據類型,session將會保存角色信息 mv.addObject("role", role); //根據名稱,session將會保存id mv.addObject("id", id); //視圖名稱,定義跳轉到一個JSP文件上 mv.setViewName("sessionAttribute"); return mv; } @RequestMapping("/sessionAttribute") public ModelAndView sessionAttr(@SessionAttribute("id") Long id) { ModelAndView mv = new ModelAndView(); Role role = roleService.getRole(id); mv.addObject("role", role); mv.setView(new MappingJackson2JsonView()); return mv; }
}
和以前的設置request屬性值的jsp腳本相似:編寫設置Session屬性的jsp腳本,而後就會跳轉到sessionAttribute控制器去處理。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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>session</title> </head> <body> <% //設置Session屬性 session.setAttribute("id", 7L); //執行跳轉 response.sendRedirect("./attribute/sessionAttribute.do"); %> </body> </html>
3.@CookieValue和@RequestHeader
能夠經過@CookieValue和@RequestHeader註解分別從Cookie和HTTP Header中讀取信息。
@RequestMapping("/getHeaderAndCookie") public String testHeaderAndCookie( @RequestHeader(value="User-Agent", required = false, defaultValue = "attribute") String userAgent, @CookieValue(value = "JSESSIONID", required = true, defaultValue = "MyJsessionId") String jsessionId) { System.out.println("User-Agent:" + userAgent); System.out.println("JSESSIONID:" + jsessionId); return "index"; }
4、攔截器
攔截器是Spring MVC 中強大的組件,它能夠在進入處理器以前作一些操做,或者在處理器完成後進行操做,甚至是在渲染視圖後進行操做。
回顧Spring MVC 執行流程:Spring MVC 會在啓動期間就經過@RequestMapping的註解解析URL和處理器的對應關係,在運行的時候經過請求找到對應的HandlerMapping,而後構建一個執行的責任鏈對象即HandlerExecutionChain對象,而HandlerExecutionChain對象中包含了handler對象,這個對象指向了控制器所對應的方法和攔截器。
1.定義攔截器
Spring 要求處理器的攔截器都要實現接口org.springframework.web.servlet.HandlerInterceptor,這個接口定義了3個方法:
2.攔截器的執行流程:
須要注意的是,當前置方法返回false時,就不會再執行後面的邏輯了。
3.開發攔截器
Spring MVC 中攔截器的設計:
其中,Spring MVC 還提供了公共攔截器HandlerInterceptorAdapter,當只想實現3個攔截器方法中的一到兩個時,能夠繼承這個公共攔截器,而後按照須要重寫須要的方法就能夠了。
建立角色攔截器RoleInterceptor類,其繼承了HandlerInterceptorAdapter公共攔截器類:
package com.ssm.chapter15.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class RoleInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.err.println("preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.err.println("postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.err.println("afterCompletion"); } }
而後還須要在Spring MVC 的配置文件dispatcher-servlet.xml中進行配置:須要聲明RoleInterceptor所在的包和類的全限定名
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/role/*.do" /> <bean class="com.ssm.chapter14.interceptor.RoleInterceptor" /> </mvc:interceptor> </mvc:interceptors>
4.多個攔截器執行的順序
假設如今有3個攔截器,在各自的方法中,分別打印攔截器方法名+編號(1,2,3)
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/role/*.do" /> <bean class="com.ssm.chapter14.interceptor.RoleInterceptor1" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/role/*.do" /> <bean class="com.ssm.chapter14.interceptor.RoleInterceptor2" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/role/*.do" /> <bean class="com.ssm.chapter14.interceptor.RoleInterceptor3" /> </mvc:interceptor> </mvc:interceptors>
(1)加入三個攔截器的preHandle方法的返回值都是true,那麼會先從第一個攔截器開始進入前置方法,前置方法是順序執行的,然後置和完成方法則是逆序運行的,這裏參考攔截器執行流程。
preHandle1
preHandle2
preHandle3
業務邏輯
postHandle3
postHandle2
postHandle1
afterCompletion3
afterCompletion2
afterCompletion1
(2)加入第二個攔截器的preHandle方法的返回值爲false,那麼後面的攔截器的preHandle方法都不會運行了,即後面的全部攔截器都不起做用。
preHandle1
preHandle2
afterCompletion1
5、驗證表單
在實際工做中,獲得數據後的第一步就是檢驗數據的正確性,若是存在錄入上的問題,通常會經過註解校驗,發現錯誤後返回給用戶,可是對於一些邏輯上的錯誤,好比購買金額=購滿數量×單價,這樣的規則就很難使用註解方式進行驗證了,這個時候可使用驗證器(Validator)規則去驗證。
1.使用JSR 303註解驗證輸入內容
Spring提供了對Bean的功能校驗,經過註解代表哪一個Bean須要進行驗證以及驗證內容:
首先,新建一個POJO類,而且根據實際須要在字段上分別進行標註:
package com.ssm.chapter14.pojo;public class Transaction { // 產品編號 @NotNull // 不能爲空 private Long productId; // 用戶編號 @NotNull // 不能爲空 private Long userId; // 交易日期 @Future // 只能是未來的日期 @DateTimeFormat(pattern = "yyyy-MM-dd") // 日期格式化轉換 @NotNull // 不能爲空 private Date date; // 價格 @NotNull // 不能爲空 @DecimalMin(value = "0.1") // 最小值0.1元 private Double price; // 數量 @Min(1) // 最小值爲1 @Max(100) // 最大值 @NotNull // 不能爲空 private Integer quantity; // 交易金額 @NotNull // 不能爲空 @DecimalMax("500000.00") // 最大金額爲5萬元 @DecimalMin("1.00") // 最小交易金額1元 private Double amount; // 郵件 @Pattern(// 正則式 regexp = "^([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)*@" + "([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)+[\\.][A-Za-z]{2,3}([\\.][A-Za-z]{2})?$", // 自定義消息提示 message = "不符合郵件格式") private String email; // 備註 @Size(min = 0, max = 256) // 0到255個字符 private String note;
/**************************getter and setter*****************************************/ }
而後建立一個表單,其中action指定了提交事後應該調用的控制器視圖:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>validate</title>
</head>
<body>
<form action = "./validate/annotation.do">
<table>
<tr>
<td>產品編號:</td>
<td><input name="productId" id="productId"/></td>
</tr>
...
<tr><td colspan="2" align="right"> <input type="submit" value="提交"/> </tr>
</table>
<form>
</body>
</html>
而後定義一個當Bean的檢驗失敗後的處理器,@Valid Transaction trans中使用@Valid註解代表這個Bean將會被檢驗,而另一個Errors的參數則是用於保存是否存在錯誤信息的。
package com.ssm.chapter14.controller; import @Controller @RequestMapping("/validate") public class ValidateController { @RequestMapping("/annotation") public ModelAndView annotationValidate(@Valid Transaction trans, Errors errors) { // 是否存在錯誤 if (errors.hasErrors()) { // 獲取錯誤信息 List<FieldError> errorList = errors.getFieldErrors(); for (FieldError error : errorList) { // 打印字段錯誤信息 System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage()); } } ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } }
測試結果:
打印結果:
fied :quantity msg:最大不能超過100
fied :productId msg:不能爲null
fied :date msg:須要是一個未來的時間
fied :userId msg:不能爲null
fied :email msg:不符合郵件格式
fied :price msg:必須大於或等於0.1
2.使用驗證器Validator規則驗證輸入內容
有時候除了簡單的輸入格式、非空型等校驗,也須要必定的業務檢驗,Spring 提供了Validator接口來實現校驗,它將在進入控制器邏輯以前對參數的合法性進行檢驗。
首先定義驗證器,必須實現Validator接口:首先驗證是不是Transaction對象,若是是,就驗證交易金額是否等於單價×數量
package com.ssm.chapter14.validator; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import com.ssm.chapter14.pojo.Transaction; public class TransactionValidator implements Validator { @Override public boolean supports(Class<?> clazz) { //判斷驗證是否爲Transaction,若是是則進行驗證 return Transaction.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { Transaction trans = (Transaction) target; //求交易金額和價格×數量的差額 double dis = trans.getAmount() - (trans.getPrice() * trans.getQuantity()); //若是差額大於0.01,則認爲業務錯誤 if (Math.abs(dis) > 0.01) { //加入錯誤信息 errors.rejectValue("amount", null, "交易金額和購買數量與價格不匹配"); } } }
而後,還須要將驗證器TransactionValidator和控制器綁定起來,Spring MVC 提供了@InitBinder註解,經過這個註解就能夠將驗證器和控制器綁定到一塊兒了:
package com.ssm.chapter14.controller; import @Controller @RequestMapping("/validate") public class ValidateController { @InitBinder public void initBinder(DataBinder binder) { // 數據綁定器加入驗證器 binder.setValidator(new TransactionValidator()); } @RequestMapping("/validator") public ModelAndView validator(@Valid Transaction trans, Errors errors) { // 是否存在錯誤 if (errors.hasErrors()) { // 獲取錯誤信息 List<FieldError> errorList = errors.getFieldErrors(); for (FieldError error : errorList) { // 打印字段錯誤信息 System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage()); } } ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } }
最後,還須要修改表單的action項爲新的當前的控制器<form action = "./validate/validator.do">,而後進行測試:在控制檯打印出:
fied :amount msg:交易金額和購買數量與價格不匹配
還須要注意的是,JSR 303註解方式和驗證器方式不能同時使用,不過能夠在使用JSR 303註解額方式獲得基本的檢驗信息後,再使用本身的方法進行驗證。
6、數據模型
視圖是業務處理後展示給用戶的內容,不過通常伴隨着業務處理返回的數據,用來給用戶查看。Spring MVC 的流程是從控制器獲取數據後,會裝載數據到數據模型和視圖中,而後將視圖名稱轉發到視圖解析器中,經過解析器解析後獲得最終視圖,最後將數據模型渲染到視圖中,展現最終的結果給用戶。
以前一直使用ModelAndView來定義視圖類型,包括JSON視圖,也用它來加載數據模型。ModelAndView有一個類型爲ModelMap的屬性model,而ModelMap繼承了LinkedHashMap<String, Object>,所以它能夠存放各類鍵值對,爲了進一步定義數據模型功能,Spring 還建立了類ExtendedModelMap,這個類實現了數據模型定義的Model 接口,而且還在此基礎上派生了關於數據綁定的類---BindAwareModelMap:
在控制器的方法中,能夠把ModelAndView、Model、ModelMap做爲參數。在Spring MVC 運行的時候,會自動初始化它們,所以能夠選擇 ModelMap 或者 Model 做爲數據模型。
@RequestMapping(value = "/getRoleByModelMap", method = RequestMethod.GET) public ModelAndView getRoleByModelMap(@RequestParam("id") Long id, ModelMap modelMap) { Role role = roleService.getRole(id); ModelAndView mv = new ModelAndView(); mv.setViewName("roleDetails"); modelMap.addAttribute("role", role); return mv; } @RequestMapping(value = "/getRoleByModel", method = RequestMethod.GET) public ModelAndView getRoleByModel(@RequestParam("id") Long id, Model model) { Role role = roleService.getRole(id); ModelAndView mv = new ModelAndView(); mv.setViewName("roleDetails"); model.addAttribute("role", role); return mv; } @RequestMapping(value = "/getRoleByMv", method = RequestMethod.GET) public ModelAndView getRoleByMv(@RequestParam("id") Long id, ModelAndView mv) { Role role = roleService.getRole(id); mv.setViewName("roleDetails"); mv.addObject("role", role); return mv; }
在瀏覽器直接輸入http://localhost:8080/Chapter14/role/getRoleByModel.do?id=8也會獲得正確的跳轉視圖。
事實上,不管是 Model 仍是 ModelMap,Spring MVC 建立的是一個BindingAwareModelMap 實例,而 BindingAwareModelMap 是一個繼承了 ModelMap 實現了 Model 接口的類,因此就有了相互轉換的功能。
7、視圖和視圖解析器
視圖是展現給用戶的內容,而在此以前,要經過控制器獲得對應的數據模型,若是是非邏輯視圖,則不會通過視圖解析器定位視圖,而是直接將數據模型渲染便結束了;而邏輯視圖則是要對其進一步解析,以定位真實視圖,這就是視圖解析器的做用了。而視圖則是把從控制器查詢回來的數據模型進行渲染,以展現給請求者查看。
1.視圖
在請求以後,Spring MVC 控制器獲取了對應的數據,綁定到數據模型中,那麼視圖就能夠展現數據模型的信息了。
視圖又分爲邏輯視圖和非邏輯視圖,好比MappingJackson2JsonView是一個非邏輯視圖,它的目的就是將數據模型轉換爲一個JSON視圖,展示給用戶,無須對視圖名字再進行下一步的解析。這其中,因爲非邏輯視圖在沒有視圖解析器的狀況下就能夠進行渲染,最終將其綁定的數據模型轉換爲JSON數據。
Spring MVC 中定義了多種視圖,它們都須要實現視圖接口--View,而View接口中主要有方法getContentType和render,其中getContentType方法表示返回一個字符串,代表給用戶什麼類型的文件響應,可使HTML、JSON、PDF等,而render方法則是一個渲染視圖的方法,其參數包括其數據模型Model,HTTP請求對象和HTTP響應對象。當控制器返回ModelAndView 的時候,視圖解析器就會解析它,而後將數據模型傳遞給 render 方法,這樣就能將數據模型渲染成各類視圖,而後經過HTTP請求兌現和HTTP響應對象展現給用戶了。
public interface View { ... String getContentType(); void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throw Exception; }
而InternalResourceView是一個邏輯視圖,對於邏輯視圖而言它須要一個視圖解析器,視圖解析器的做用也就是,經過前綴和後綴加上視圖名稱就可以找到對應的JSP文件,而後把數據模型渲染到JSP文件中,這樣便能展示視圖給用戶了。
<!-- 定義視圖解析器 --> <!-- 找到Web工程/WEB-INF/JSP文件夾,且文件結尾爲jsp的文件做爲映射 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
2.視圖解析器
對於邏輯視圖而言,把視圖名稱轉換爲邏輯視圖是一個必備的過程,InternalResourceView會加載到 Spring MVC 的視圖解析器列表中去,當返回ModelAndView的時候,Spring MVC 就會在視圖解析器列表中遍歷,找到對應的視圖解析器去解析視圖。
視圖解析器的定義以下,其中viewName表示傳遞進來的視圖名稱,而Locale類型的locale是國際化對象。
public interface ViewResolver { View resolveViewName(String viewName, Local locale) throws Exception; }
有時候在控制器中並無返回一個 ModelAndView, 而是隻返回了一個字符串,它也可以渲染視圖,由於視圖解析器生成了對應的視圖:
@RequestMapping(value = "/index", method = RequestMethod.GET) public String index(@RequestParam("id") Long id, ModelMap model) { Role role = roleService.getRole(id); model.addAttribute("role", role); return "roleDetails"; }
3.實例:Excel視圖的使用
需求:用戶經過輸入URL,而後經過GET請求下載到保存了數據庫中全部的查詢記錄的Excel表
(1)選擇視圖類
對於Excel而言,Spring MVC 所推薦的是使用AbstractXlsView,根據視圖類的關係能夠看到,AbstractXlsView繼承了AbstractView類,而AbstractView類又實現了View接口。
因爲AbstractXlsView是抽象類,所以須要實現其中的抽象方法buildExcelDocument:
protected abstract void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception);
其中,model表示數據模型,workbook表示POI workbook對象,這個方法的主要任務是建立一個Workbook,它要用到POI的API。
(2)自定義導出接口
目前的需求是導出全部就是信息,可是未來或許還須要增長其餘的導出功能,所以,先定義一個接口,這個接口的功能是讓開發者自定義生成Excel的規則。
package com.ssm.chapter14.view; import java.util.Map; import org.apache.poi.ss.usermodel.Workbook; public interface ExcelExportService { /*** * 生成exel文件規則 * @param model 數據模型 * @param workbook excel workbook */ public void makeWorkBook(Map<String, Object> model, Workbook workbook); }
(3)定義Excel視圖
即Excel視圖類:對於導出來講,還須要一個下載文件名稱,因此還須要定義一個fileName屬性。因爲該視圖不是一個邏輯視圖,因此無需視圖解析器也能夠運行它。
對於buildExcelDocument方法來講,其最後一行纔是關鍵:excelExpService.makeWorkBook(model, workbook);,意思就是調用ExcelExportService接口的makeWorkBook方法使用自定義的規則進行Excel建立。便可以根據須要進行自定義生成Excel的規則。
package com.ssm.chapter14.view;
import
public class ExcelView extends AbstractXlsView { // 文件名 private String fileName = null; // 導出視圖自定義接口 private ExcelExportService excelExpService = null; // 構造方法1 public ExcelView(ExcelExportService excelExpService) { this.excelExpService = excelExpService; } // 構造方法2 public ExcelView(String viewName, ExcelExportService excelExpService) { this.setBeanName(viewName); }
/******** getter and setter **********/
@Override protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { // 沒有自定義接口 if (excelExpService == null) { throw new RuntimeException("導出服務接口不能爲null!!"); } // 文件名不爲空,爲空則使用請求路徑中的字符串做爲文件名 if (!StringUtils.isEmpty(fileName)) { // 進行字符轉換 String reqCharset = request.getCharacterEncoding(); reqCharset = reqCharset == null ? "UTF-8" : reqCharset; fileName = new String(fileName.getBytes(reqCharset), "ISO8859-1"); // 設置下面文件名 response.setHeader("Content-disposition", "attachment;filename=" + fileName); } // 回調接口方法,使用自定義生成Excel文檔 excelExpService.makeWorkBook(model, workbook); } }
(4)定義控制器
其中ExcelView ev = new ExcelView(exportService());將獲得ExcelView類型的視圖ev,而後mv.addObject("roleList", roleList);加入數據模型,最後mv.setView(ev);將視圖設置爲ev。
ExcelView ev = new ExcelView(exportService());中exportService()方法就是(5)中定義的ExcelExportService接口的實現類的方法。
@RequestMapping(value = "/export", method = RequestMethod.GET) public ModelAndView export() { //模型和視圖 ModelAndView mv = new ModelAndView(); //Excel視圖,並設置自定義導出接口 ExcelView ev = new ExcelView(exportService()); //文件名 ev.setFileName("全部角色.xlsx"); //設置SQL後臺參數 RoleParams roleParams = new RoleParams(); //限制1萬條 PageParams page = new PageParams(); page.setStart(0); page.setLimit(10000); roleParams.setPageParams(page); //查詢 List<Role> roleList = roleService.findRoles(roleParams); //加入數據模型 mv.addObject("roleList", roleList); mv.setView(ev); return mv; }
(5)定義ExcelExportService接口的實現:這裏是使用了Lambda表達式實現的,看起來比較高級。
@SuppressWarnings({ "unchecked"}) private ExcelExportService exportService() { //使用Lambda表達式自定義導出excel規則 return (Map<String, Object> model, Workbook workbook) -> { //獲取用戶列表 List<Role> roleList = (List<Role>) model.get("roleList"); //生成Sheet Sheet sheet= workbook.createSheet("全部角色"); //加載標題 Row title = sheet.createRow(0); title.createCell(0).setCellValue("編號"); title.createCell(1).setCellValue("名稱"); title.createCell(2).setCellValue("備註"); //便利角色列表,生成一行行的數據 for (int i=0; i<roleList.size(); i++) { Role role = roleList.get(i); int rowIdx = i + 1; Row row = sheet.createRow(rowIdx); row.createCell(0).setCellValue(role.getId()); row.createCell(1).setCellValue(role.getRoleName()); row.createCell(2).setCellValue(role.getNote()); } }; }
(6)測試:在瀏覽器中輸入:http://localhost:8080/Chapter14/role/export.do就能夠下載到一個名爲「全部角色.xlsx」的Excel文件,打開該文件,能夠看到正確顯示了數據庫中的記錄:
8、上傳文件
Spring MVC 爲上傳文件提供了良好的支持。首先 Spring MVC 的文件上傳是經過MultipartResolver處理的,對於MultipartResolver而言它只是一個接口,它有兩個實現類:CommonMultipartResovler和StandardMultipartResolver。其中,StandardMultipartResolver不須要引入任何第三方包便可實現。不管使用哪一個類,都須要配置一個MultipartResolver
1.MultipartResolver配置
(1)經過註解配置StandardMultipartResolver
對於StandardMultipartResolver來講,其構造方法沒有參數,所以很容易對其進行初始化。
@Bean(name = "multipartResolver") public MultipartResolver initMultipartResolver() { return new StandardServletMultipartResolver(); }
可是,僅僅這樣配置是不夠的,還須要對上傳文件進行配置,例如限制單個文件的大小,設置上傳路徑等,爲了進行設置,能夠在Spring MVC 初始化的時候對MultipartResolver進行配置:
若是須要經過Java配置 Spring MVC 的初始化,只須要繼承AbstractAnnotationConfigDispatcherServletInitializer 類就能夠了,經過繼承它就能夠進行註解配置了,這個類中能夠覆蓋customizeRegistration方法,它是一個用於初始化DispatcherServlet設置的方法。
package com.ssm.chapter15.config; import javax.servlet.MultipartConfigElement; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //Spring IoC容器配置 @Override protected Class<?>[] getRootConfigClasses() { //能夠返回Spring的Java配置文件數組 return new Class<?>[] {}; } //DispatcherServlet的URI映射關係配置 @Override protected Class<?>[] getServletConfigClasses() { //能夠返回Spring的Java配置文件數組 return new Class<?>[] { WebConfig.class }; } //DispatchServlet[修改成:DispatcherServlet]攔截請求匹配 @Override protected String[] getServletMappings() { return new String[] { "*.do" }; } /** * @param dynamic Servlet動態加載配置 */ @Override protected void customizeRegistration(Dynamic dynamic) { //文件上傳路徑 String filepath = "d:/mvc/upload"; //5MB Long singleMax = (long) (5*Math.pow(2, 20)); //10MB Long totalMax = (long) (10*Math.pow(2, 20)); //配置MultipartResolver,限制請求,單個文件5MB,總共文件10MB dynamic.setMultipartConfig(new MultipartConfigElement(filepath, singleMax, totalMax, 0)); } }
其中,customizeRegistration方法中設置了MultipartResolver的屬性,包括文件路徑、單個文件大小、所有文件大小。
(2)經過XML配置StandardMultipartResolver
還能夠經過在Web.xml中實現對MultipartResolver的初始化,而後經過XML或者註解生成一個AbstractAnnotationConfigDispatcherServletInitializer便可。
<!--MultipartResolver參數 --> <multipart-config> <location>e:/mvc/uploads/</location> <max-file-size>5242880</max-file-size> <max-request-size>10485760</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config>
(3)經過註解配置CommonMultipartResovler,可是這種方式須要依賴於第三方的包
@Bean(name = "multipartResolver") public MultipartResolver initCommonsMultipartResolver() { //文件上傳路徑 String filepath = "d:/mvc/uploads"; //5MB Long singleMax = (long) (5 * Math.pow(2, 20)); //10MB Long totalMax = (long) (10 * Math.pow(2, 20)); CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setMaxUploadSizePerFile(singleMax); multipartResolver.setMaxUploadSize(totalMax); try { multipartResolver.setUploadTempDir(new FileSystemResource(filepath)); } catch (IOException e) { e.printStackTrace(); } return multipartResolver; }
2.提交上傳文件表單
須要將enctype="multipart/form-data設置成這樣,不然 Spring MVC 會解析失敗。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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 method="post" action="./file/upload.do" enctype="multipart/form-data" > <input type="file" name="file" value="請選擇上傳的文件"/> <input type="submit" value="提交"/> </form> </body> </html>
3.與之對應,須要有一個upload控制器方法
package com.ssm.chapter15.controller; import @Controller @RequestMapping("/file") public class FileController implements ApplicationContextAware { @RequestMapping("/upload") public ModelAndView upload(HttpServletRequest request) { // 進行轉換 MultipartHttpServletRequest mhsr = (MultipartHttpServletRequest) request; // 得到請求上傳的文件 MultipartFile file = mhsr.getFile("file"); // 設置視圖爲JSON視圖 ModelAndView mv = new ModelAndView(); mv.setView(new MappingJackson2JsonView()); // 獲取原始文件名 String fileName = file.getOriginalFilename(); // 目標文件 File dest = new File(fileName); try { // 保存文件 file.transferTo(dest); // 保存成功 mv.addObject("success", true); mv.addObject("msg", "上傳文件成功"); } catch (IllegalStateException | IOException e) { // 保存失敗 mv.addObject("success", false); mv.addObject("msg", "上傳文件失敗"); e.printStackTrace(); } return mv; }
}
4.測試
項目目錄爲:
其中,採用1(1)中的配置,然而首先須要建立目錄,d:/mvc/upload,而後在瀏覽器中輸入:http://localhost:8080/Chapter15.2/file.jsp
選擇文件後,點擊提交,跳轉到下面的頁面,同時,d:/mvc/upload下也發現了以前選擇的文件:
5.問題分析
這裏會有一個問題,就是控制器中的upload方法的參數是HttpServletRequest request,這樣會形成API侵入,即調用了Servlet的API
@RequestMapping("/upload") public ModelAndView upload(HttpServletRequest request) {....}
解決方法是,將參數修改爲MultipartFile或者Part類對象,這樣作的好處是把代碼從Servlet API 中解放出來,這體現了Spring 的思惟,即高度的解耦合性。
下面兩個方法都經過了測試。
// 使用MultipartFile @RequestMapping("/uploadMultipartFile") public ModelAndView uploadMultipartFile(MultipartFile file) { // 定義JSON視圖 ModelAndView mv = new ModelAndView(); mv.setView(new MappingJackson2JsonView()); // 獲取原始文件名 String fileName = file.getOriginalFilename(); file.getContentType(); // 目標文件 File dest = new File(fileName); try { // 保存文件 file.transferTo(dest); mv.addObject("success", true); mv.addObject("msg", "上傳文件成功"); } catch (IllegalStateException | IOException e) { mv.addObject("success", false); mv.addObject("msg", "上傳文件失敗"); e.printStackTrace(); } return mv; } // 使用Part @RequestMapping(value="/uploadPart", method=RequestMethod.POST) public ModelAndView uploadPart(Part file) { ModelAndView mv = new ModelAndView(); mv.setView(new MappingJackson2JsonView()); // 獲取原始文件名 String fileName = file.getSubmittedFileName(); try { // 保存文件 file.write("d:/mvc/upload/" + fileName); mv.addObject("success", true); mv.addObject("msg", "上傳文件成功"); } catch (IllegalStateException | IOException e) { mv.addObject("success", false); mv.addObject("msg", "上傳文件失敗"); e.printStackTrace(); } return mv; }