接下來就是Spring的各個處理細節了,不管框架如何瘋轉其實咱們處理請求的流程是不變的,設計到的操做也是固定的,舉個例子,當咱們要實現一個登錄功能時:html
建立一個用於處理登陸請求的Servletjava
實現doget等其餘http方法(一些狀況可能根據業務須要限制請求方法)web
從request對象中取出數據spring
處理編碼sql
驗證參數是否符合要求數據庫
對參數數據類型進行轉換(須要時)apache
開始業務邏輯處理(登陸)json
可能須要操做session來完成數組
組織響應給數據,多是html多是json,tomcat
Header與cookie的處理
整個SpringMVC其實就是幫咱們對上面的操做進行封裝,固然了SpringMVC也提供了更多的功能,如國際化..
在handler方法中咱們能夠添加一下參數,用於獲取一些特殊的對象:
model 是框架幫咱們建立好的的Model對象,若使用該參數做爲返回的model,則須要修改方法返回值爲String用於指定視圖名稱;
案例:
@RequestMapping("/courseList2.action") public String courseList(Model model) { model.addAttribute("courses", courseService.selectCourseList()); return "courses.jsp"; } @RequestMapping("/courseList3.action") public String courseList(ModelMap model, HttpSession session,HttpServletRequest request,HttpServletResponse response) { System.out.println(request); System.out.println(response); System.out.println(session.getId()); model.addAttribute("courses", courseService.selectCourseList()); return "courses.jsp"; }
在使用Servlet開發的過程當中咱們會頻繁的調用request.getAttribute來獲取請求參數,參數較少時還沒什麼,一旦參數較多的時候就會產生大量的冗餘代碼,SpringMVC提供了多種以簡化獲取參數的過程的方法
在handler方法中添加與前臺參數名稱和類型匹配的參數,框架會自動解析參數傳入handler方法中;
案例:
假設咱們要修改課程信息的功能,首先要獲取原始信息
@RequestMapping("/edit.action") public String edit(Model model,Integer id) { Course course = courseService.selectCourseByID(id); model.addAttribute("course",course); return "edit.jsp"; }
支持的參數類型:
整形:Integer、int 字符串:String 單精度:Float、float 雙精度:Double、double 布爾型:Boolean、boolean
當先後臺參數名稱不匹配時能夠@RequestParam註解進行自定義映射;
註解參數:
案例:
@RequestMapping("/edit.action") public String edit(Model model,@RequestParam("id") Integer iid) { Course course = courseService.selectCourseByID(iid); model.addAttribute("course",course); return "edit.jsp"; }
注意:參數類型能夠是基礎類型也能夠是包裝類型,建議使用包裝類型,這樣能夠保證爲獲取到參數時不會由於null沒法轉換爲基礎類型而致使的異常;
edit.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head></head> <form action="updateCourse.action" method="post"> <input name="id" value="${course.id}" hidden="hidden"/> <input name="name" value="${course.name}"/> <input name="teachName" value="${course.teachName}"/> <%-- <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>--%> <input name="score" type="number" value="${course.score}"/> <input name="hours" type="number" value="${course.hours}"/> <input type="submit"> </form> <body> </body> </html>
當參數個數很是多時上面的方法就顯得麻煩了,SpringMVC支持將參數映射到一個實體類;
在handler方法中添加任意類型實體類做爲參數; 一樣的只有參數名稱和實體屬性一致時才能映射成功;
咱們繼續完善修改功能,如今要獲取修改後的內容了:
@RequestMapping("/updateCourse.action") public String update(Course course) { courseService.updateCourse(course); return "/courseList.action"; }
上面的例子中出現了中文亂碼問題,請求方法爲post, 只須要在request中設置編碼方式便可,可是此時參數已經被框架解析了,咱們在handler中經過request設置以及不生效了,因此咱們須要在請求到達SpringMVC以前就進行處理,這就用到了之前學過的過濾器了;
好消息是SpringMVC以及提供了過濾器,咱們只須要配置到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>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 編碼過濾器 END-->
須要注意的是,該過濾器只對post生效,若是是get亂碼則仍是須要修改tomcat的server.xml或是經過代碼從ISO-8859-從新編碼爲UTF-8
String username = request.getParameter("username"); username = new String(username.getBytes("iso8859-1"), "utf-8"); //從新編碼
咱們將edit.jsp中的開課標籤取消註釋,而後測試會發現系統給出了400異常,查看控制檯能夠看到如下信息:
意思是框架沒法將String類型的請求參數轉換爲須要的Date類型,這就須要,這是由於日期格式多種多樣,每一個地區不一樣,因此這須要咱們本身來實現轉換;
實現convert接口便可做爲轉換器,該接口的兩個兩個泛型表示輸入源類型和輸出目標類型;
public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String s) { SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd"); try { return sm.parse(s); } catch (ParseException e) { e.printStackTrace(); } return null; } }
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.kkb.converter.StringToDateConverter"/> </set> </property> </bean> <mvc:annotation-driven conversion-service="conversionService"/>
使用@DateTimeFormat能夠實現上面xml相同的配置:
public class Course { private Integer id; private String name; private String teachName; @DateTimeFormat(pattern = "yyyy-MM-dd") private Date startTime; }
注意:註解做用在實體類對應的屬性上,且配置文件必須添加<mvc:annotation-driven>
當須要將參數映射到實體類的關聯對象中時,也稱爲包裝類型;
例如:在課程對象中有一個用戶對象,表示這是某個用戶的課程;前臺須要同時傳遞課程對象的屬性,和用戶對象的屬性,後臺就須要要用一個包裝類型來接收,即一個包裝了用戶對象的課程對象; 再說的簡單點,即課程對象中包含一個用戶對象;
在前臺須要指出關聯對象的屬性名稱,如:用戶.name
實體:
public class Course { private Integer id; private String name; private String teachName; private Date startTime; private Integer score; private Integer hours; private User user;//新添加的User類屬性 set/get....
handler:
@RequestMapping("/updateCourse.action") public String update(Course course) { courseService.updateCourse(course); return "/courseList.action"; }
jsp:
<form action="updateCourse.action" method="post"> <input name="id" value="${course.id}" hidden="hidden"/> <input name="name" value="${course.name}"/> <input name="teachName" value="${course.teachName}"/> <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/> <input name="score" type="number" value="${course.score}"/> <input name="hours" type="number" value="${course.hours}"/> <input name="user.username"/> <!-- 新添加的參數--> <input type="submit"> </form>
一些狀況下,某一參數可能會有多個值,例如要進行批量刪除操做,要刪除的id會有多個,那就須要將參數映射到一個數組中;HttPServletRequest本來就支持獲取數組參數,SpringMVC僅是幫咱們作了一個類型轉換;
1.修改頁面添加多選框:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> </head> <body> <form action="deleteCourses"> <table border="1"> <tr> <th>選擇</th> <th>名稱</th> <th>講師</th> <th>開課日期</th> <th>學分</th> <th>課時</th> <th>操做</th> </tr> <c:forEach items="${courses}" var="course"> <tr> <td> <input type="checkbox" name="ids" value="${course.id}"> </td> <td>${course.name}</td> <td>${course.teachName}</td> <td> <fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/> </td> <td>${course.score}</td> <td>${course.hours}</td> <td> <a href="editCourse?id=${course.id}">修改</a> </td> </tr> </c:forEach> </table> <input type="submit" value="批量刪除"> </form> </body> </html>
2.handler方法:
@RequestMapping("/deleteCourses") public String deleteCourses(Integer[] ids){ courseService.deleteCourses(ids); return "/getCourses"; }
3.service方法:
public void deleteCourses(Integer[] ids) { for (Integer id : ids){ courseMapper.deleteByPrimaryKey(id); } }
當請求參數包含多個對象的屬性數據,是須要使用list來接收,一般用在批量修改批量添加等;
list映射要求參數名稱爲對象屬性[下標].屬性名稱
,同時handler中使要用包裝類型來接收;
如下是實現一個批量修改課程信息的功能;
1.修改頁面中的td,使得每個td均可以編輯:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> </head> <body> <form action="deleteCourses" id="fm"> <table border="1"> <tr> <th>選擇</th><th>名稱</th><th>講師</th><th>開課日期</th><th>學分</th><th>課時</th><th>操做</th> </tr> <c:forEach items="${courses}" var="course" varStatus="status"> <tr> <td><input type="checkbox" name="ids" value="${course.id}"></td> <td><input value="${course.name}" name="courses[${status.index}].name"/></td> <td><input value="${course.teachName}" name="courses[${status.index}].teachName"/></td> <td><input value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>' name="courses[${status.index}].startTime"/></td> <td><input value="${course.score}" name="courses[${status.index}].score"/></td> <td><input value="${course.hours}" name="courses[${status.index}].hours"/></td> <input hidden="hidden" name="courses[${status.index}].id" value="${course.id}"> <td><a href="editCourse?id=${course.id}">修改</a></td> </tr> </c:forEach> </table> <input type="submit" value="批量刪除"> <input type="button" onclick='function updates() { document.getElementById("fm").action = "updateCourses" document.getElementById("fm").submit() } updates()' value="批量修改"> </form> </body> </html>
2.包裝類型:
public class RequestPack { //用於接收參數列表的list private List<Course> courses; public List<Course> getCourses() { return courses; } public void setCourses(List<Course> courses) { this.courses = courses; } public RequestPack(List<Course> courses) { this.courses = courses; } }
3.handler方法:
@RequestMapping("/updateCourses") public String updateCourses(RequestPack data){ courseService.updateCourses(data.getCourses()); return "/getCourses"; }
4.service方法:
public void updateCourses(List<Course> courses) { for (Course course:courses){ courseMapper.updateByPrimaryKey(course); } }
強調:list只能映射到包裝類型中,沒法直接映射到handler參數上
錯誤案例:
@RequestMapping("/updateCourses") public String updateCourses(RequestPack data, ArrayList<Course> courses){ courseService.updateCourses(data.getCourses()); return "/getCourses"; }
文件上傳是web項目中很是常見的需求,SpringMVC使用了apache開源的兩個庫用於處理文件上傳,因此在編寫代碼前咱們須要先導入下面兩個依賴包:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
假設須要實現一個上傳圖片的功能,須要如今數據庫中添加一個字段用於存儲圖片的路徑,同時不要忘記修改pojo以及mapper文件,使之與數據庫字段對應
1.頁面增長input 用於提交文件,並修改表單的enctype爲multipart/form-data
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head></head> <form action="updateCourse" method="post" enctype="multipart/form-data"> <input name="id" value="${course.id}" hidden="hidden"/> 名稱:<input name="name" value="${course.name}"/> <br/> 講師:<input name="teachName" value="${course.teachName}"/> <br/> <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/> <br/> 學分:<input name="score" type="number" value="${course.score}"/> <br/> 課時:<input name="hours" type="number" value="${course.hours}"/> <br/> <c:if test="${course.pic != null}"> <img src="${pageContext.servletContext.contextPath}${course.pic}" style="width: 100px;height: 100px"> </c:if> 圖片:<input name="picFile" type="file"/><br/> <!-- 新增input--> <br/> <input type="submit"> </form> <body> </body> </html>
2.在mvc配置文件中添加multipart解析器,(頁面上傳文件都是以,multipart編碼方式)
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
3.handler方法中添加MultipartFile類型的參數
@RequestMapping("/updateCourse") public String updateCourse(Course course, MultipartFile picFile) throws IOException { /*** * 1.得到文件,取出文件後綴 * 2.生成惟一標識, * 3.寫入文件到指定路徑 * 4.存儲文件路徑到數據庫 */ System.out.println(picFile.getName()); String suffix = picFile.getOriginalFilename().substring(picFile.getOriginalFilename().lastIndexOf(".")); String fileName = UUID.randomUUID() + suffix; String basepath = getClass().getClassLoader().getResource(".").getPath(); System.out.println(basepath); picFile.transferTo(new File(basepath+"../../images/"+fileName)); course.setPic("/images/"+fileName); courseService.update(course); return "/getCourses"; }
注意:實際開發中都是存儲到文件服務器,不會放在項目裏
4.靜態資源處
若web.xml中DispatcherServlet的URLmapping 爲/ 則還須要在SpringMVC中添加靜態資源配置
<mvc:resources mapping="/images/**" location="/images/"/> <!--當請求地址爲/images/開頭時(不管後面有多少層目錄),做爲靜態資源 到/images/下查找文件-->
若URLMapping爲*.action 或相似其餘的時則無需處理,由於Tomcat會直接查找webapp下的資源,不會交給DispatcherServlet
一些狀況下咱們可能須要對請求進行限制,好比僅容許POST,GET等...
RequestMapping註解中提供了多個參數用於添加請求的限制條件
案例:
@RequestMapping(value = "/editCourse",method = RequestMethod.POST,headers = {"id"},params = {"name"},consumes = {"text/plain"})
爲了簡化書寫,MVC還提供了集合路徑和方法限制的註解,包括常見的請求方法:
PostMapping GetMapping DeleteMapping PutMapping 例: @PostMapping("/editCourse")