當處理器對請求處理完畢後,向其它資源進行跳轉時,有兩種跳轉方式:請求轉發與重定向。而根據所要跳轉的資源類型,又可分爲兩類:跳轉到頁面與跳轉到其它處理器html
注意,對於請求轉發的頁面,能夠是WEB-INF中頁面;而重定向的頁面,是不能爲WEB-INF中頁的。由於重定向至關於用戶再次發出一次請求,而用戶是不能直接訪問 WEB-INF 中資源的java
SpringMVC 框架把原來 Servlet 中的請求轉發和重定向操做進行了封裝。如今可使用簡單的方式實現轉發和重定向。web
forward:表示轉發,實現 request.getRequestDispatcher("xx.jsp").forward()spring
redirect:表示重定向,實現 response.sendRedirect("xxx.jsp")數據庫
處理器方法返回 ModelAndView 時,需在 setViewName()指定的視圖前添加 forward:,且此時的視圖再也不與視圖解析器一同工做,這樣能夠在配置瞭解析器時指定不一樣位置的視圖。視圖頁面必須寫出相對於項目根的路徑。forward 操做不須要視圖解析器。
處理器方法返回 String,在視圖路徑前面加入 forward: 視圖完整路徑數組
仍是第一個案例中瀏覽器
/** * 處理器方法返回ModelAndView,實現轉發forward * 語法: setViewName("forward:視圖文件完整路徑") * forward特色: 不和視圖解析器一同使用,就當項目中沒有視圖解析器,不受視圖解析器的限制了 */ @RequestMapping(value = "/doForward.do") public ModelAndView doForward(String name , Integer age){ ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); // mv.setViewName("show"); //顯式轉發 // mv.setViewName("forward:/WEB-INF/view/show.jsp"); // 配置了視圖解析器,可是這個文件不在view目錄之下就可使用這樣的方法,由於forward不受視圖解析器的限制 mv.setViewName("forward:/hello.jsp"); return mv; }
在處理器方法返回的視圖字符串的前面添加 redirect:,則可實現重定向跳轉
spring-mvc
/** * 處理器方法返回ModelAndView,實現重定向redirect * 語法:setViewName("redirect:視圖完整路徑") * redirect特色: 不和視圖解析器一同使用,就當項目中沒有視圖解析器 * * 框架對重定向的操做: * 1.框架會把Model中的簡單類型的數據,轉爲string使用,做爲hello.jsp的get請求參數使用。 * 目的是在 doRedirect.do 和 hello.jsp 兩次請求之間傳遞數據 * * 2.在目標hello.jsp頁面可使用參數集合對象 ${param}獲取請求參數值 * ${param.myname} * * 3.重定向不能訪問/WEB-INF資源 */ @RequestMapping(value = "/doRedirect.do") public ModelAndView doRedirect(String name , Integer age){ ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); //重定向 mv.setViewName("redirect:/hello.jsp"); // 至關於這樣,內部做爲get請求,經過param能夠獲取的到 //http://localhost:8080/08_forword/hello.jsp?myname=lisi&myage=22 //重定向不能訪問/WEB-INF資源 // mv.setViewName("redirect:/WEB-INF/view/show.jsp"); return mv; }
SpringMVC 框架處理異常的經常使用方式:使用@ExceptionHandler 註解處理異常tomcat
使用註解@ExceptionHandler 能夠將一個方法指定爲異常處理方法。該註解只有一個可選屬性 value,爲一個 Class<?>數組,用於指定該註解的方法所要處理的異常類,即所要匹配的異常服務器
而被註解的方法,其返回值能夠是 ModelAndView、String,或 void,方法名隨意,方法參數能夠是 Exception 及其子類對象、HttpServletRequest、HttpServletResponse 等。系統會自動爲這些方法參數賦值
MyUserException是父類
package com.md.exception; /** * @author MD * @create 2020-08-14 20:31 */ public class MyUserException extends Exception { public MyUserException() { super(); } public MyUserException(String message) { super(message); } } //------------------------------- package com.md.exception; /** * 當用戶的年齡有異常拋出AgeException * @author MD * @create 2020-08-14 20:33 */ public class AgeException extends MyUserException { public AgeException() { super(); } public AgeException(String message) { super(message); } } //---------------------------- package com.md.exception; /** * 當用戶的姓名有異常拋出NameException * @author MD * @create 2020-08-14 20:32 */ public class NameException extends MyUserException { public NameException() { super(); } public NameException(String message) { super(message); } }
/** *此時拋出的異常是下面兩個異常的父類 */ @RequestMapping(value = "/some.do") public ModelAndView doSome(String name , Integer age) throws MyUserException { ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); // 根據請求參數拋出異常 if (!"md".equals(name)){ throw new NameException("姓名不正確"); } if (age == null || age > 100){ throw new AgeException("年齡有誤"); } mv.setViewName("show"); return mv; }
package com.md.handler; import com.md.exception.AgeException; import com.md.exception.NameException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; /** * @author MD * @create 2020-08-14 20:38 */ /** * @ControllerAdvice : 控制器加強(也就是說給控制器類增長功能--異常處理功能) * 位置:在類的上面。 * 特色:必須讓框架知道這個註解所在的包名,須要在springmvc配置文件聲明組件掃描器。 * 指定@ControllerAdvice所在的包名 */ @ControllerAdvice public class GlobalException { //定義方法,處理髮生的異常 /* 處理異常的方法和控制器方法的定義同樣, 能夠有多個參數,能夠有ModelAndView, String, void,對象類型的返回值 形參:Exception,表示Controller中拋出的異常對象。 經過形參能夠獲取發生的異常信息。 @ExceptionHandler(異常的class):表示異常的類型,當發生此類型異常時, 由當前方法處理 */ @ExceptionHandler(value = NameException.class) public ModelAndView doNameException(Exception ex) { // 處理NameException異常 /* 異常發生處理邏輯: 1.須要把異常記錄下來, 記錄到數據庫,日誌文件。 記錄日誌發生的時間,哪一個方法發生的,異常錯誤內容。 2.發送通知,把異常的信息經過郵件,短信,微信發送給相關人員。 3.給用戶友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg", "姓名是md,其餘用戶不能訪問"); mv.addObject("ex", ex); mv.setViewName("nameError"); return mv; } @ExceptionHandler(value = AgeException.class) public ModelAndView doAgeException(Exception ex) { // 處理AgeException異常 /* 異常發生處理邏輯: 1.須要把異常記錄下來, 記錄到數據庫,日誌文件。 記錄日誌發生的時間,哪一個方法發生的,異常錯誤內容。 2.發送通知,把異常的信息經過郵件,短信,微信發送給相關人員。 3.給用戶友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg", "年齡過大"); mv.addObject("ex", ex); mv.setViewName("AgeError"); return mv; } // 這個只能有一個,也就是萬能的異常處理方法 // 處理其餘異常,NameException、AgeException以外的異常,也就是除了自定義以外的異常都能處理 @ExceptionHandler public ModelAndView doOtherException(Exception ex) { // 處理OtherException異常 ModelAndView mv = new ModelAndView(); mv.addObject("msg", "404"); mv.addObject("ex", ex); mv.setViewName("defaultError"); return mv; } }
nameError.jsp
<%-- Created by IntelliJ IDEA. User: MD Date: 2020/8/14 Time: 20:54 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>nameError.jsp</h1> <h2>提示:${msg}</h2> <h2>系統異常信息:${ex.message}</h2> </body> </html>
<h1>ageError.jsp</h1> <h2>提示:${msg}</h2> <h2>系統異常信息:${ex.message}</h2> <%--和上面都同樣,這裏就省略了--%> <h1>defaultError.jsp</h1> <h2>提示:${msg}</h2> <h2>系統異常信息:${ex.message}</h2>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--聲明組件掃描器--> <context:component-scan base-package="com.md.controller"/> <!--聲明springmvc框架中的視圖解析器,幫助開發人員設置視圖文件路徑--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前綴:視圖文件的路徑--> <property name="prefix" value="/WEB-INF/view/" /> <!--後綴:視圖文件的擴展名--> <property name="suffix" value=".jsp"/> </bean> <!-- 處理異常的兩個步驟 1. 註冊組件掃描器,也便是@ControllerAdvice註解所在的包名 2. 註冊註解驅動, --> <context:component-scan base-package="com.md.handler"/> <mvc:annotation-driven/> </beans>
這樣就能夠了
SpringMVC 中的 Interceptor 攔截器是很是重要和至關有用的,它的主要做用是攔截指定的用戶請求,並進行相應的預處理與後處理。
Controller類
@RequestMapping(value = "/some.do") public ModelAndView doSome(String name , Integer age) { System.out.println("-------MyController的doSome()"); ModelAndView mv = new ModelAndView(); mv.addObject("myname",name); mv.addObject("myage",age); mv.setViewName("show"); return mv; }
仍是在以前的項目中,定義個普通類,做爲攔截器
package com.md.handler; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Date; /** * 攔截器類,攔截用戶的請求 * @author MD * @create 2020-08-14 21:18 */ public class MyInterceptor implements HandlerInterceptor { // 快捷鍵:ctrl + O private long btime = 0; /* * preHandle叫作預處理方法。 * 重要:是整個項目的入口,門戶。 當preHandle返回true 請求能夠被處理。 * preHandle返回false,請求到此方法就截止。 * * 參數: * Object handler : 被攔截的控制器對象 * 返回值boolean * true:請求是經過了攔截器的驗證,能夠執行處理器方法 * 執行順序 攔截器: preHandle() -------MyController的doSome() 攔截器: postHandle() 攔截器: afterCompletion() * * false:請求沒有經過攔截器的驗證,請求到達攔截器就截止了。 請求沒有被處理 * 僅僅輸出這一句話, * 攔截器: preHandle() * * * 特色: * 1.方法在控制器方法(MyController的doSome)以前先執行的。 * 用戶的請求首先到達此方法 * * 2.在這個 方法中能夠獲取請求的信息, 驗證請求是否符合要求。 * 能夠驗證用戶是否登陸, 驗證用戶是否有權限訪問某個鏈接地址(url)。 * 若是驗證失敗,能夠截斷請求,請求不能被處理。 * 若是驗證成功,能夠放行請求,此時控制器方法才能執行。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { btime = System.currentTimeMillis(); System.out.println("攔截器: preHandle()"); // 計算的業務邏輯,根據計算的結果,返回true或者false // 給瀏覽器一個反饋 // request.getRequestDispatcher("/tips.jsp").forward(request,response); // // return false; return true; } /* postHandle:後處理方法。 參數: Object handler:被攔截的處理器對象MyController ModelAndView modelAndView:處理器方法的返回值 特色: 1.在處理器方法以後執行的(MyController.doSome()) 2.可以獲取處處理器方法的返回值ModelAndView,能夠修改ModelAndView中的 數據和視圖,能夠影響到最後的執行結果。 3.主要是對原來的執行結果作二次修正, ModelAndView mv = MyController.doSome(); postHandle(request,response,handler,mv); */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("攔截器: postHandle()"); // 對原來的doSome執行的結果進行調整 if (modelAndView != null){ modelAndView.addObject("mydate" , new Date()); modelAndView.setViewName("other"); } } /* afterCompletion:最後執行的方法 參數 Object handler:被攔截器的處理器對象 Exception ex:程序中發生的異常 特色: 1.在請求處理完成後執行的。框架中規定是當你的視圖處理完成後,對視圖執行了forward。就認爲請求處理完成。 2.通常作資源回收工做的, 程序請求過程當中建立了一些對象,在這裏能夠刪除,把佔用的內存回收。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("攔截器: afterCompletion()"); long etime = System.currentTimeMillis(); System.out.println("計算從preHandle到請求處理完成的時間:"+(etime - btime )); } }
建立springmvc的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--聲明組件掃描器--> <context:component-scan base-package="com.md.controller"/> <!--聲明springmvc框架中的視圖解析器,幫助開發人員設置視圖文件路徑--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前綴:視圖文件的路徑--> <property name="prefix" value="/WEB-INF/view/" /> <!--後綴:視圖文件的擴展名--> <property name="suffix" value=".jsp"/> </bean> <!-- 聲明攔截器: 攔截器能夠一個或者多個--> <mvc:interceptors> <!--聲明第一個攔截器--> <mvc:interceptor> <!-- 指定攔截的請求url地址, page就是url地址,可使用通配符 ** , 表示任意的字符,文件或者多級目錄--> <!-- http://localhost:8080/user/xxx--> <!--<mvc:mapping path="/user/**"/>--> <mvc:mapping path="/**"/> <!--聲明攔截器對象--> <bean class="com.md.handler.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans>
攔截器中方法與處理器方法的執行順序
再定義一個攔截器
package com.md.handler; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("111111111111攔截器: preHandle()"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("11111111111111111攔截器: postHandle()"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("111111111111111攔截器: afterCompletion()"); } } //--------------------------------------- package com.md.handler; public class MyInterceptor2 implements HandlerInterceptor { // 快捷鍵:ctrl + O @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("2222222222222222攔截器: preHandle()"); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("2222222222222222攔截器: postHandle()"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("122222222222222222攔截器: afterCompletion()"); } } //---------------------------------------
多個攔截器的註冊,在springmvc的配置文件
<!-- 聲明攔截器: 攔截器能夠一個或者多個 在框架中保存是在一個ArrayList , 先聲明的在前面, --> <mvc:interceptors> <!--聲明第一個攔截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--聲明攔截器對象--> <bean class="com.md.handler.MyInterceptor"/> </mvc:interceptor> <!--聲明第二個攔截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--聲明攔截器對象--> <bean class="com.md.handler.MyInterceptor2"/> </mvc:interceptor> </mvc:interceptors> <!-- 返回的都是true,執行的順序--> <!--111111111111攔截器: preHandle()--> <!--2222222222222222攔截器: preHandle()--> <!-- -------MyController的doSome()--> <!--2222222222222222攔截器: postHandle()--> <!--11111111111111111攔截器: postHandle()--> <!--122222222222222222攔截器: afterCompletion()--> <!--111111111111111攔截器: afterCompletion()--> <!-- 第一個true,第一個false,執行順序--> <!--111111111111攔截器: preHandle()--> <!--2222222222222222攔截器: preHandle()--> <!--111111111111111攔截器: afterCompletion()--> <!-- 返回的都是false,執行的順序--> <!--111111111111攔截器: preHandle()--> </beans>
當有多個攔截器時,造成攔截器鏈。攔截器鏈的執行順序,與其註冊順序一致。須要再次強調一點的是,當某一個攔截器的 preHandle()方法返回 true 並被執行到時,會向一個專門的方法棧中放入該攔截器的 afterCompletion()方法