當用戶發送請求時,根據Spring MVC的請求處理流程,前端控制器會請求處理器映射器返回一個處理器,而後請求處理器適配器之心相應的處理器,此時處理器映射器會調用Spring Mvc 提供的參數綁定組件將請求的key/value 數據綁定到Controller處理器方法對應的形參上。Spring MVC使用Converter轉換器能夠進行各類類型的轉換,也可自定義Converter轉換器,Spring MVC默認轉換器支持的類型有HttpServletRequest、HttpServletResponse、HttpSession、Model、ModelMap。其中Model是一個接口,ModelMap是一個接口實現,做用是將model數據填充到request。前端
//localhost:8080/springMvcNext/product/infoa?id=1 @RequestMapping("infoa") public String productInfoa(Model model, Integer id) { model.addAttribute("message", "productid:" + id); return "product/info"; }
備註:若是url中參數名不是id,則不會綁定成功,須要經過使用註解RequestParam綁定參數web
自定義類型傳遞,使用pojo傳遞(Product)spring
@RequestMapping(value="infob",method = RequestMethod.POST) public String productInfob(Model model, Product product) { model.addAttribute("message", "product-price:" + product.getPrice()+"product-name:" + product.getProductName()); return "product/info"; }
經過RequestParam註解綁定參數形參名與入參不一致的參數,RerquestParam有三個參數屬性,value參數名,指定要綁定的入參名,required是否必須,默認爲false,defaultValue屬性,用於沒有傳遞時賦默認值。服務器
//http://localhost:8080/springMvcNext/product/info?productId=1&name=fgsg @RequestMapping("info") public String productInfo(Model model, @RequestParam(name = "name", defaultValue = "test") String productName, @RequestParam(required = true) Integer productId) { model.addAttribute("message", "name:" + productName + " productid:" + productId); return "product/info"; }
經過RequestHeader註解獲取請求頭的信息,RequestHeader一樣有三個參數屬性value,required,defaultvaluecookie
// http://localhost:8080/springMvcNext/product/info2 // 輸出產品信息:browser:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36 language:zh-CN,zh;q=0.9 @RequestMapping("info2") public String productInfo2(Model model, @RequestHeader("User-Agent") String browser, @RequestHeader(value = "Accept-Language", required = false, defaultValue = "null") String language) { model.addAttribute("message", "browser:" + browser + " language:" + language); return "product/info"; }
經過CookieValue註解獲取請求頭的信息mvc
//http://localhost:8080/springMvcNext/product/info3 //輸出:產品信息:JSESSIONID:0FD3AFA5E445DADACBC1F07568970FEC @RequestMapping("info3") public String productInfo3(Model model, @CookieValue("JSESSIONID") String cookie) { model.addAttribute("message", "JSESSIONID:" + cookie); return "product/info"; }
使用HttpServletRequest獲取請求參數,當客戶端經過HTTP協議訪問服務器時,HTTP請求頭中的全部信息都封裝在這個對象中,開發人員經過這個對象的方法,能夠得到客戶這些信息,HttpServletRequest能夠用於參數解析,Cookie讀取,http請求字段,文件上傳app
@RequestMapping("info4") public String productInfo(String houseUnitInfo, HttpServletRequest request, HttpServletResponse response) throws IOException { String requestStr = charReader(request); System.out.println(requestStr); return "product/info"; } private String charReader(HttpServletRequest request) throws IOException { BufferedReader br = request.getReader(); String str, wholeStr = ""; while ((str = br.readLine()) != null) { wholeStr += str; } // System.out.println(wholeStr); return wholeStr; }
測試:框架
結果:jsp
返回ModelAndView能夠指定視圖名和model數據,ModelAndView提供的addObject方法來給這個模型添加數據,添加的是一個鍵值對的數據post
@RequestMapping("info5") public ModelAndView productInfo5() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("product/detail"); modelAndView.addObject("message", "return modelandview"); //modelAndView.addObject("xxx", "yyy"); return modelAndView; }
返回void,Map,Model 時,返回對應的邏輯視圖名稱就是請求url,仍然遵循:prefix前綴+視圖名稱 +suffix後綴組成
//1.返回Map// 訪問視圖: /springMvcNext/WEB-INF/view/product/detail.jsp @RequestMapping("detail") public Map<String, Object> detail2313() { Map<String, Object> map = new HashMap<String, Object>(); map.put("message", "product detail"); return map; } //2.返回void //返回void時,則響應的視圖頁面對應爲訪問地址 //訪問視圖: /springMvcNext/WEB-INF/view/product/info6.jsp @RequestMapping("info6") public void productInfo6() { }
但輸出流中存在輸出內容時,則不會去查找視圖,而是將輸入流中的內容直接響應到客戶端,響應的內容類型是純文本
@RequestMapping("info7") public void productInfo7(HttpServletResponse response) throws IOException { response.getWriter().write("<h2>void method</h2>");//直接相應結果 } @RequestMapping("info8") public void productInfo8(HttpServletResponse response) throws IOException { response.sendRedirect("detail"); //重定向 訪問:http://localhost:8080/springMvcNext/product/detail }
//3. 返回Model model對象會用於頁面渲染,視圖路徑使用方法名,與void相似。示例代碼以下: @RequestMapping("info9") public Model productInfo9(Model model) { model.addAttribute("message", "product detail"); return model; }
返回視圖名:Controller類方法返回字符串能夠指定邏輯視圖名,經過視圖解析器解析爲物理視圖地址。
@RequestMapping("info10") public String productInfo10(Model model) { model.addAttribute("message", "productInfo10"); return "product/detail"; }
Spring MVC中對與WEB-INF目錄下面的JSP頁面,不能直接經過URL訪問。須要經過轉發的方式,而咱們通常都是在控制器中作轉發映射,對應一些咱們不須要其餘操做的JSP頁面,咱們可使用<mvc:view-controller path=""/>來配置,這樣就能夠不用再控制器中再去作轉發映射。
<!-- 配置直接進行轉發的頁面,無須進入handler方法 --> <mvc:view-controller path="home" /> <mvc:view-controller path="order/info" />
訪問:http://localhost:8080/springMvcNext/order/info 和 http://localhost:8080/springMvcNext/home 不通過處理器
重定向:Spring mvc中能夠在返回的結果前加上一個前綴「redirect:」,能夠重定向到一個指定的頁面也能夠是另外一個action
轉發:Springmvc中返回結果前加「foword」前綴,注意:轉發是一次請求(相同的request),地址欄的URL不會改變
//重定向 //訪問:http://localhost:8080/springMvcNext/product/redirecttest 時Url將跳轉http://localhost:8080/springMvcNext/product/info10?redirectparas=test+redirect @RequestMapping("redirecttest") public String redirecttest(Model model) { model.addAttribute("redirectparas", "test redirect"); //帶參數跳轉 return "redirect:/product/info10"; } //轉發 //訪問http://localhost:8080/springMvcNext/product/forwardtest url不會跳轉 @RequestMapping("forwardtest") public String forwardtest(Model model){ model.addAttribute("forwardparas", "test forward"); //帶參數跳轉 return "forward:/product/info10"; }
Spring MVC中經過使用@controlleradvice + @ ExceptionHandler 兩個註解能夠實現全局的異常捕捉。
@ExceptionHandler註解的做用是當出現其定義的異常時進行處理的方法,其可使用springmvc提供的數據綁定,好比注入HttpServletRequest等,還能夠接受一個當前拋出的Throwable對象
@ControllerAdvice 註解能夠把異常處理器應用到全部控制器 @Controller ,而不是@Controller註解的單個控制器,該異常處理器對當前控制器的全部方法有效;若是單獨某個控制器須要自定義處理異常,不用頂層的異常處理器,能夠在當前控制器內用 @ExceptionHandler 註解 ,這樣當前控制器的異常處理就在當前類中。
備註:使用ControllerAdvice註解類裏面的異常的處理的優先級低於直接定義在處理方法的類中
實現一個異常處理器:
@ControllerAdvice public class ExceptionHandlers { @ExceptionHandler({ArithmeticException.class}) public ModelAndView toException(Exception e){ ModelAndView mv = new ModelAndView("home"); System.out.println("gobal handler exception"); //雖然不能使用Map往request中存值,可是可使用下面的方法 mv.addObject("error", e); System.out.println(e); return mv; } }
控制器
@Controller @RequestMapping("exception") public class ExceptionController { // 示例1 @RequestMapping("test") public ModelAndView test() { System.out.println(10/0); //拋異常 return new ModelAndView("order/info", "message", "test exception"); } }
Spring MVC提供了Interceptor攔截機制,用於請求的預處理和後處理。在Spring MVC中定義一個攔截器有兩種方法:第一種是實現HandlerInterceptor接口,或者繼承實現了HandlerInterceptor接口的類例如(HandlerInterceptorAdapter);第二種方法是實現Spring的WebRequestInterceptor接口(該接口是針對請求的攔截器接口,接口方法參數中沒有response),或者繼承實現了WebrequestInterceptor的類。兩種方式都是在Handler的執行週期內進行攔截操做。
若是要實現HandlerInterceptor接口,須要實現三個方法,preHandle、postHandle、afterCompletion
preHandle方法在執行Handler方法以前執行,返回false表示攔截請求,不在執行後續邏輯,能夠用來作權限,日誌等。
postHandle方法在執行Handler方法以後,返回modelAndView以前執行,因爲該方法會在DispatcherServlet進行返回視圖渲染以前被調用,因此此方法多被用於同一處理返回視圖,例如將公用的模型數據添加到視圖,或者根據其餘狀況制定公用的視圖。
afterCompletion方法在執行完Handler以後執行,因爲是在Controller方法執行完畢後執行該方法,因此該方法適合進行統一的異常或者日誌處理操做。
實現HandlerInterceptor接口以後須要在Spring的類加載配置文件中配置攔截器實現類,才能使攔截器起到攔截的效果。HandlerInterceptor類加載配置有兩種方式,分別是」針對HandlerMapping配置」和 全局配置。
針對HandlerMapping配置須要在某個處理器映射器配置中將攔截器做爲參數配置進去,以後經過此處理器映射器的handler就會使用配置好的攔截器,配置以下:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="hInterceptor1" /> <ref bean="hInterceptor2" /> </list> </property> <property name="order" value="1"></property> </bean> <bean id="hInterceptor1" class="com.sl.interceptors.TestInterceptor"></bean> <bean id="hInterceptor2" class="com.sl.interceptors.TestOrderInterceptor"></bean>
全局配置,springmvc框架將配置的全局攔截器注入到每一個HandlerMapping中。
<!-- 配置自定義的攔截器 --> <mvc:interceptors> <bean class="com.sl.interceptors.TestInterceptor"></bean> </mvc:interceptors>
實現一個攔截器:
@Component public class TestInterceptor implements HandlerInterceptor { /** * 當目標方法執行以前,執行此方法,返回false,則再也不執行後續邏輯postHandle、afterCompletion */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("First preHandle 最早執行.."); return true; } /** * 執行目標方法以後,渲染視圖以前調。 在轉向jsp頁面以前, 能夠對請求域中的屬性,或者視圖進行修改 */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("First postHandle 執行目標方法以後,渲染視圖以前調。 在轉向jsp頁面以前,"); } /** * 在渲染視圖以後被調用,能夠進行日誌處理 */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("First afterCompletion 渲染視圖以後調用"); } }
運行controller則能夠看到攔截器執行記錄。
若是定義多個攔截器,則執行順序以下:
1. preHandle是按配置文件中的順序執行的
2. postHandle是按配置文件中的倒序執行的
3. afterCompletion是按配置文件中的倒序執行的
測試驗證:
攔截器的指定範圍:配置攔截器時能夠根據須要制定攔截器做用範圍,針對特定處理器或方法進行攔截。
<!-- 配置攔截器 --> <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根節點下則攔截全部的請求 --> <!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 --> <mvc:interceptors> <mvc:interceptor>
<!-- 指定攔截器做用路徑 --> <mvc:mapping path="/product/*" /> <bean class="com.sl.interceptors.TestInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/order/*" /> <bean class="com.sl.interceptors.TestOrderInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
<mvc:exclude-mapping path=""/> 表示針對該Path不攔截 ,<mvc:mapping path=""/> 表示針對該Path攔截,Path可使用通配符。