一:配置springMVC開發環境 1.1.配置文件的helloworld 1.2.基於註解的helloworld 二:@RequestMapping詳解 2.1.類的@RequestMapping註解 2.2.請求方式的設置 2.3.參數的規定與請求頭的規定設置 2.4.@RequestMapping映射中的通配符 2.5.@RequestMapping與@PathVariable註解的一塊兒使用 2.6.Rest風格的URL 三:獲取http請求中的信息 3.1.獲取請求中的參數,@RequestParam 3.2.獲取請求頭的信息,@RequestHeader 3.3.獲取Cookie的信息,@CookieValue 3.4.使用Pojo來獲取請求中的大量參數 四:Servlet原生API 4.1.使用Servlet原生API 4.2.使用Servlet原生API的原理(部分springMVC的源代碼) 4.3.springMVC支持哪些原生API 五:頁面中的傳值 5.1.簡單的使用ModelAndView來向頁面傳遞參數 5.2.使用參數Map來向頁面傳值 5.3.使用@SessionAttributes註解,把值放到session域中 5.4.@SessionAtrribute引起的異常 六:@ModelAtrribute註解詳解 6.1.簡單介紹@ModelAtrribute及其運行流程 6.2.@ModelAtrribute源代碼分析 6.3.解決@ModelAtrribute註解中,map集合的鍵與執行目標方法的參數名不一致的狀況 七:轉發與重定向 7.1<mvc:view-controller>標籤 7.2.自定義視圖 7.4.視圖的重定向操做 八:數據的格式化 8.1.日期的格式化 8.2.數字的格式化 8.3.數據的校驗 8.4.JSR303的驗證類型 8.5.傳遞json類型的數據 8.6.文件上傳 九:攔截器 9.1.第一個攔截器 9.2.攔截器的指定範圍 9.3.攔截器的使用順序 十:異常的處理 10.1.使用springMVC的註解@ExceptionHandler來處理異常 10.2.如何把異常傳遞到頁面顯示 10.3.處理的優先級 10.4.全局異常處理
任何mvc的框架,都是須要在web.xml中進行核心的配置的,struts2是一個過濾器,而SpringMVC則是一個servlet,可是配置方法大同小異,都是在web.xml中進行配置,配置方法以下:
<servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置DispatcherServlet的一個初始化參數:配置SpringMVC配置文件位置和名稱,這個也能夠不配置,若是不配置,則默認是WEB-INFO下的springMVC-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:abc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <!-- 攔截全部url --> <url-pattern>/</url-pattern> </servlet-mapping>
再而後就是配置springMVC-servlet.xml,這個文件名以前說過,能夠自定義路徑與名稱,若是不自定義,則必定要放在WEB-INFO下,注意引入正確的命名空間,而後加入核心分解解析器,就是解析url的請求,相似於stuts的配置文件,配置以下:
html
<!-- 視圖分解解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 這是前綴 --> <property name="prefix" value="/"></property> <!-- 這是後綴 --> <property name="suffix" value=".jsp"></property> <!-- 在spring的控制器中,返回的是一個字符串,那麼請求的路徑則是,前綴+返回字符串+後綴 --> </bean>
而後就是正式使用SpringMVC了,咱們一般是用註解的方式,由於註解真的太好用了,省事好多,可是這裏也簡單介紹一下使用xml配置文件的方式來寫一個簡單的helloworldjava
首先新建一個類,這個類要實現Controller接口,而後實現它的一個ModelAndView方法,代碼以下:git
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class HelloWroldController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("spring ModelAndView"); return new ModelAndView("/welcome"); } }
而後在springMVC-servlet.xml中配置一個bean:配置方法以下:web
<bean name="/test" class="controller.HelloWroldController"></bean>
這種配置和struts2實際上是同樣的,name屬性就是瀏覽器請求的url,而class就是它處理的類,而運行的方法,就是上面所繼承的接口,而後在瀏覽器輸入http://localhost:8080/springMVC/test,就能夠跳轉到welcome.jsp下,這是根據以前配置的視圖分解解析器的先後綴前該方法返回的字符串拼接而成的。正則表達式
使用註解就要簡單得多了,首先是在springMVC-servlet.xml中配置掃描的包,這樣spring就會自動根據註解來描述一些特定的註解,而後把這些bean裝載進入spring容器中,配置以下:spring
<!-- 定義掃描的包 --> <context:component-scan base-package="zxj"></context:component-scan>
而後,在指定的包,或者其子包下新建一個類,代碼以下:數據庫
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @Controller是控制器的註解 * @author jie */ @Controller public class Control { /** * 這裏配置了RequestMapping註解,就至關於配置了struts2的action,也就至關於url地址 * 其返回的地址爲spingMVC-servlet.xml中,前綴+spinrg+後綴 * @return */ @RequestMapping("/helloworld") public String helloworld(){ return "spring"; } }
這樣就不用再寫配置文件,只要加上相應的註解就行,好比說這裏的helloworld方法,只要在瀏覽器輸入http://localhost:8080/project/hellowrold,就能夠進入spring.jsp頁面json
剛纔看到了@RequestMapping這個註解是用於給一個方法加上url的請求地址,不過,這個註解不只僅能夠加在方法上面,也能夠加在類上面,代碼以下圖所示:數組
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/springMVC") @Controller public class SpringMVCTest { private static final String SUCCESS = "success"; @RequestMapping("/testRequestMapping") public String testRequestMapping(){ System.out.println("testRequestMapping"); return "spring"; } }
如上代碼如示,若是同時在類與方法上面同時加上@RequestMapping註解的話,那麼URL確定不會是以前的那種寫法了,如今要寫成類的註解加上方法的註解,就是有點相似struts2中,<package>的nameplace屬性,那如如上代碼,URL應該爲http://localhost:8080/project/springMVC/testReuqestMapping瀏覽器
像咱們日常提交一個表單,確定會有get,與post這兩個經常使用的請求,那麼在springMVC中要如何設置呢?很簡單,也是在@RequestMapping中使用,不過須要在方法的註解上面使用,代碼以下:
/** * 使用method屬性來指定請求方式 * @return */ @RequestMapping(value="/testMethod",method=RequestMethod.POST) public String testMethod(){ System.out.println("testMethod"); return "spring"; }
這下在from表單中,或者異步請求中,就須要以post作爲請求,否則會報錯的,由於這裏已經設置爲了post,若是客戶端再請求get,將會報錯。
咱們都知道http請求會有着請求參數與請求頭消息,那麼在springMVC裏面,是能夠規範這些信息的,首先給一段代碼示例:
/** * 表明着這個請求中參數必須包含username參數,並且age不能等於10 * 並且請求頭中,請求的語言爲中文,時區爲東八區,不然就報錯,不容許請求 * @return */ @RequestMapping(value="testParamsAndHeader",params={"username","age!=10"},headers={"Accept-Language=zh-CN,zh;q=0.8"}) public String testParamsAndHeader(){ System.out.println("testParamHeader"); return "spring"; }
不管是params,仍是headers,均可以包含多個參數,以逗號相隔就行,若是不知足寫了的條件,則就會報錯,不容許請求資源。其次這兩個屬性還支持一些簡單的表達式:
user 表示請求中,必須包含user的參數 !user 表示請求中,不能包含user的參數 user!=admin 表示請示中,user參數不能爲admin user,age=10 表示請求中必須包含user,age這兩個參數,並且age要等於10
在@RequestMapping的value屬性中,還支持Ant格式的能配符:
? 匹配url中的任意一個字符 * 匹配url中的任意多個字符 ** 匹配url中的多層路徑
下面舉幾個例子:
/user/*/createUser 匹配/user/abcd/createUser /user/**/createUser 匹配/user/aa/bb/cc/createUser /user/?/createUser 匹配/user/a/createUser
這裏就不做代碼演示了,相信你們一看就懂,由於這種通配符真的太常見了
springMVC很靈活,它能夠獲取URL地址中的值,而後看成變量來輸出,這裏要使用@PathVariable註解,故名思意,就是路徑變量的意思,一般的話,@PathVariable要使用,是必定要與@RequestMapping一塊兒使用的,下面給出代碼示例:
/** * 先在方法上面映射URL,這裏面能夠用一個佔位符,而後在參數中用@PathVariable來獲取此佔位符的值 * @param id * @return */ @RequestMapping("testPathVariable/{id}") public String testPathVariable(@PathVariable("id") Integer id){ System.out.println("PathVariable:"+id); return "spring"; }
先在括號中加上註解,其中value就是@RequestMapping中佔位符的聲明,而後加上數據類型和定義的變量,這樣就能夠對其進行使用了
一般的話,表單有着post,與get提交方式,而rest風格的URL,則有着get,post,put,delete這四種請求方式,可是瀏覽器倒是隻支持get與post,因此咱們可使用springMVC,把它進行轉換,咱們要利用org.springframework.web.filter.HiddenHttpMethodFilter這個類,這是一個過濾器,咱們首先要在web.xml中配置它,請配置在第一個位置,否則的話,可能會先進入其它的過濾器,配置代碼以下:
<!-- 配置org.springframework.web.filter.HiddenHttpMethodFilter,能夠把post請求轉爲delete或put請求 --> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
如上的配置,這個過濾器則會攔截全部的請求,咱們能夠看一下org.springframework.web.filter.HiddenHttpMethodFilter的部分源代碼:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String paramValue = request.getParameter(this.methodParam);//this.methodParam是一個final常量,爲_method if (("POST".equals(request.getMethod())) && (StringUtils.hasLength(paramValue))) { String method = paramValue.toUpperCase(Locale.ENGLISH); HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method); filterChain.doFilter(wrapper, response); } else { filterChain.doFilter(request, response); } }
當這個過濾器攔截到一個請求時,就會先拿到這個請求的參數,它要知足兩個條件,第一,瀏覽器發出的請求爲post請示,第二,它還要有一個參數,參數名爲_method,而它的值,則能夠爲get,post,delete,put,此時過濾器就會把post請求轉換成相應的請求,否則的話就不進行轉換,直接請求。至於添加_method參數的話,則可使用hidden隱藏域,或者使用?拼接參數均可以。
下面就該是控制器的方法了,在本處第2小點中,有講到@RequestMapping的請求方式的設置,只要把這個請求方式設置成對應的請求就行,好比說轉換成了DELETE請求,則@RequestMapping也要寫成對應的DELETE請求,否則會出錯,示例代碼以下:
@RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE) public String testRestDelete(@PathVariable("id") Integer id){ System.out.println("test DELETE:"+id); return "spring"; } @RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT) public String testRestPut(@PathVariable("id") Integer id){ System.out.println("test put:"+id); return "spring"; }
此時,就能夠正確轉換請求方式了。
在獲取相似這種:http://localhost:8080/project/test?user=a&password=2這種請求參數時,就須要用@RequestParam這個註解來獲取,代碼以下:
/** * 使用@RequestParam註解來獲取請求參數 * value屬性 參數名 * required 是否非空,默認爲true,若是請求中無此參數,則報錯,能夠設置爲false * defaultValue 默認值,當瀏覽器沒有帶此參數時,該值會有一個默認值 * @param username * @param password * @return */ @RequestMapping(value="/testRequestParam") public String testRequestParam(@RequestParam("username") String username, @RequestParam(value="password",required=false,defaultValue="我是默認值") String password ){ System.out.println(username); System.out.println(password); return "spring"; }
如上面註釋所寫,它經常使用這三個屬性,value是參數名,可是若是隻寫了參數名的話,請求時,就必須帶此參數,否則就會報錯。若是把required屬性設置爲false,就可使得該參數不傳,還有defaultValue屬性,此屬性能夠當瀏覽器沒有傳此參數時,給這個參數一個默認值
以前也有提到過http的請求頭的信息,那麼這裏就可使用@RequestHeader註解來獲取請求頭的信息,它的使用方法與上面的@RequestParam是徹底同樣的,一樣擁有value,requied,defaultValue這三個屬性,而有表明的做用也是同樣的,下面給出代碼示例:
/** * 使用@RequestHeader註解來獲取請求頭的信息 * 它與@RequestParam的使用方法是同樣的,都有着三個參數,value,required,defaultValue * @param header * @return */ @RequestMapping("testRequestHeader") public String testRequestHeader(@RequestHeader(value="Accept-Language",required=false,defaultValue="null") String header){ System.out.println(header); return "spring"; }
由於它的用法與本章第一點的獲取請求參數的用法同樣,因此這裏就不做過多的說明,詳細能夠查看@RequestParam的用法
在開發中,有不少狀況都會用到Cookie,這裏可使用@CookieValue註解來獲取,其使用方法與@RequestParam與@RequestHeader同樣,這裏就不過多敘述,給出示例代碼:
/** * 使用@CookieValue註解來獲取瀏覽器傳過來的Cookie * 它與@RequestHeader與@RequestParam的用法同樣,也是有着required與default兩個屬性來指定是否爲空與默認值 * @param value * @return */ @RequestMapping("testCookieValue") public String testCookieValue(@CookieValue(value="JSESSIONID",required=true,defaultValue="no") String value){ System.out.println("CookieValue:"+value); return "spring"; }
它也有着三個屬性,value,required,defaultValue,分別對應Cookie名,是否非空,默認值。
若是http請求中只有一兩個參數,那麼使用@RequestParam還能夠,可是若是一個請求中帶有着大量的參數,那麼這樣就有點麻煩了,那麼springMVC就可使用Pojo對象來獲取此次請求中的全部參數,而且所有封裝到這個對象裏面,這種方式相似struts2的ModelDriver<T>,相信使用過struts2的同窗都清楚,這種方式極其簡便,下面一邊給代碼,一邊解釋,首先給出請求的處理方法:
/** * 使用Pojo對象來傳遞參數,由於若是一個請求中包含了大量的參數,那麼所有用@RequestParam來作確定太麻煩<br> * 這裏能夠在參數中定義一個實體類,實體類中對應着屬性,springMVC就會把從瀏覽器獲取到的參數所有封裝到這個對象裏面<br> * 並且這裏面的參數能夠爲空,並且還支持級聯參數(就是指下面User類中的屬性還對應了一個Address的類) * @param user * @return */ @RequestMapping("testPojo") public String testPojo(User user){ System.out.println(user); return "spring"; }
這裏無需使用其它的註解,只須要在這個處理方法中加上一個類就行,那麼springMVC就會自動把請求參數封裝到你寫好的類中,並且這種封裝還支持級聯操做,什麼是級聯操做呢?就是User類中的屬性還有着另外的一個類,下面給出User類的代碼:
public class User { private String username; private String password; private String email; private String age; private Address address; ...省略get,set方法 }
由上面可見,其中不只有普通的屬性,還有着一個Address的類,咱們再來看一下Address類的代碼:
public class Address { private String province; private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } ...省略get,set方法 }
能夠很清楚的看清User類與Address類的關係,那麼像這種關係的對象,在瀏覽器form表單中的name屬性該如何寫呢?Address類中的字段,要加上address,好比address.province,或者address.city,其它的屬性,就直接寫User類中的屬性就能夠了。而這裏爲何Address變成了小寫的呢?其實這並非什麼命名規則,而是我在User類中就是這麼定義的
private Address address;
這下沒有什麼問題了吧,咱們再來看一下瀏覽器表單是怎麼寫的:
<form action="${pageContext.request.contextPath}/testPojo" method="post"> username:<input type="text" name="username"/><br/> password:<input type="password" name="password"/><br/> email:<input type="text" name="email"/><br/> age:<input type="text" name="age"/><br/> <!-- POJO支持級聯屬性,因此name屬性是以下的寫法,address.province,意思就是有一個address的類,類裏面有province屬性 --> province:<input type="text" name="address.province"/><br/> city:<input type="text" name="address.city"/><br/> <input type="submit" value="提交" /> </form>
如上表單元素就能夠看到表單的name屬性是如何與User類對應的,其Address類中的屬性,就以address.city。
下面來講一下這種請求方式的特色:
1.簡便,不須要大量的@RequestParam註解。
2.與struts2的ModelDriver的用法差很少,只不過ModelDriver是接口,整個類裏面全部的方法均可以使用。而springMVC中這個Pojo對象的做用僅僅是當前的處理方法中。
3.這種Pojo的使用中,瀏覽器的參數能夠爲空,就是能夠不傳參數,也不會報錯,不像@RequestParam,若是不指定requried=false的話,還會報錯。
在咱們日常使用springMVC中,雖說springMVC已經幫咱們作了不少工做了,可是咱們實際中仍是會要用到Servlet的原生API,那這個時候要如何獲得Servlet的原生對象呢?這裏與struts2不一樣,springMVC是在方法中聲明對應的參數來使用這些對象,而struts2則是調用相應的方法來獲得這些對象。固然,對於沒有學過struts2的同窗,能夠忽略,下面給出代碼示例:
@RequestMapping("testServletAPI") public String testServletAPI(HttpServletRequest request,HttpServletResponse response,User user){ System.out.println(request+" "+response); System.out.println(user); System.out.println("testServletAPI"); return "spring"; }
如上代碼所示,直接在對應的處理方法裏面聲明這些須要使用的對象就能夠了,那若是同時要使用Pojo來得到請求參數怎麼辦呢?這個不用擔憂,照常使用就好了,如上代碼所示,一樣聲明瞭一個User類來接收參數,並不會有任何的影響。
若是你想問,springMVC中的處理方法,裏面能夠支持哪些Servlet的原生API對象呢?或者你又想問,爲何能夠照常的使用Pojo來獲取請求參數呢?那麼這裏,咱們先來看一下springMVC的源代碼,而後再做解釋:
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest .getNativeRequest(HttpServletRequest.class); HttpServletResponse response = (HttpServletResponse) webRequest .getNativeResponse(HttpServletResponse.class); if ((ServletRequest.class.isAssignableFrom(parameterType)) || (MultipartRequest.class.isAssignableFrom(parameterType))) { Object nativeRequest = webRequest .getNativeRequest(parameterType); if (nativeRequest == null) { throw new IllegalStateException( "Current request is not of type [" + parameterType.getName() + "]: " + request); } return nativeRequest; } if (ServletResponse.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; Object nativeResponse = webRequest .getNativeResponse(parameterType); if (nativeResponse == null) { throw new IllegalStateException( "Current response is not of type [" + parameterType.getName() + "]: " + response); } return nativeResponse; } if (HttpSession.class.isAssignableFrom(parameterType)) { return request.getSession(); } if (Principal.class.isAssignableFrom(parameterType)) { return request.getUserPrincipal(); } if (Locale.class.equals(parameterType)) { return RequestContextUtils.getLocale(request); } if (InputStream.class.isAssignableFrom(parameterType)) { return request.getInputStream(); } if (Reader.class.isAssignableFrom(parameterType)) { return request.getReader(); } if (OutputStream.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; return response.getOutputStream(); } if (Writer.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; return response.getWriter(); } return super.resolveStandardArgument(parameterType, webRequest); }
從這裏就能夠說明了一個問題了,springMVC首先會經過反射技術獲得這個方法裏面的參數(源代碼沒有貼上,有興趣的能夠自行查看springMVC的源代碼),而後比較這些參數的類型,是否與上面的九個類型想匹配,若是匹配成功,則返回這個對象,請注意,是與對象類型相匹配,而不是與形參名做匹配,因此這樣,就不會使得Pojo沒法工做了
其實從4.2中的源代碼中也是能夠看到了,這裏支持九種個對象,對應關係分別是:
ServletRequest -- ServletRequest
ServletResponse -- ServletResponse
HttpSession -- HttpSession Principal -- request.getUserPrincipal() Locale -- RequestContextUtils.getLocale(request); InputStream -- request.getInputStream(); Reader -- request.getReader(); OutputStream -- response.getOutputStream(); Writer -- response.getWriter();
實際開發中,總要大量的向頁面中傳遞後臺的數據,那麼存放數據的方法也有多種多樣,好比說存到request中,你們都知道是使用request.setAtrribute()來向request域中傳遞值,可是實際上springMVC給咱們封裝了更好的方法,那就是使用ModelAndView。
首先,方法的返回值,該由String變成ModelAndView,而後在處理方法中new一個ModelAndView對象,而後返回這個對象就能夠了,對象中能夠增長返回頁面的字符,也能夠向這個對象裏面傳遞參數,如今給出一個簡單的示例代碼:
/** * 使用ModelAndView來對頁面傳值<br> * 注意:org.springframework.web.servlet.ModelAndView; * 而不是org.springframework.web.portlet.ModelAndView; * @return */ @RequestMapping("testModelAndView") public ModelAndView testModelAndView(){ String viewName = "spring"; ModelAndView modelAndView = new ModelAndView(viewName); //添加模型數據到ModelAndView中,數據存在request中 modelAndView.addObject("time", new Date()); modelAndView.setViewName("spring"); return modelAndView; }
如上代碼如示,咱們可使用構造方法給它傳一個值,那就是它最終要返回的頁面的值,或者使用setViewName方法來給它一個返回頁面的名字。使用addObject方法來給這個模型添加數據,這是一個鍵值對的數據,而後返回這個ModelAndView對象。
能夠在執行方法中定義一個Map參數,而後在方法中,向map添加內容,而後在頁面中根據map的鍵來取對應的值,也是存在request域中,下面給出代碼示例:
/** * 使用參數map來向頁面傳值<br> * 頁面中取值的方法${requestScope.names},${requestScope.nation} * @param map * @return */ @RequestMapping("testMap") public String testMap(Map<String, Object> map ){ map.put("names", Arrays.asList("zxj","lj","ly")); map.put("nation", "han"); return "spring"; }
這裏面就是在方法中有着一個Map類型的參數,其實不只僅能夠是Map類型,還能夠是Model與ModelMap類型的,由於最終傳入的根本就不是Map,而是
org.springframework.validation.support.BindingAwareModelMap
其實5.1和5.2所訴的內容,雖然是把後臺數據傳遞到前臺,可是所有都是放置到request域中,這裏講訴一下使用@SessionAtrributes註解,把後臺數據同時放到session中。
首先介紹一下這個註解,這個註解只能放在類上面,而不能放在方法上面。它有兩個屬性,一個是value屬性,一個是types屬性,這兩個數據都是數組,因此能夠聲明多個,其實不管是value屬性,仍是types屬性,均可以把數據同時放到session域中,只不過value屬性對應的是執行方法中的map集合的鍵,只要是對應的鍵,那麼其值,也會同時被傳遞到session域中(這裏之因此加上「同時」這個詞,是由於它會同時存到request與session中),而types屬性,則是放置類的集合,只要map集合中存的是該類的數據,則也會同時被放到request中,下面給示例代碼:
/** * @Controller是控制器的註解 * @SessionAtrributes是session的註解,使用它,就能把map中對應的值,放到session域中 * @author 朱小杰 */ @SessionAttributes(value={"user"},types={String.class}) @Controller public class Control { /** * 在類上面使用@SessionAtrributes註解,把map集合中的值放到session域中 * @param map * @return */ @RequestMapping("testSessionAtrributes") public String testSessionAtrributes(Map<String,Object> map) { User user = new User("zxj","123","1@qq.com","11"); map.put("user", user); map.put("school", "xinxi"); return "spring"; } }
如上代碼所示,先是在類上面使用了@SessionScope註解,而後同時使用了value與types屬性,第一個value屬性的值"user",則是testSessionAtrributes方法中map集合中的"user"的鍵,因此這一個鍵值對會被同時放入session域中,而第二個types的屬性中的String.class,則是表明着這個類了,意思就是說,只要是map集合中放的String類型的數據,都會被放到session中。
注意:使用了value屬性後,這個屬性也就必須存在,意思就是說,必須有一個map,而後有一個user的鍵,否則會報錯,固然,這個是能夠解決的,後面會詳細講到。
上一講說到使用@SessionAtrribute來修飾一個類,就能夠把一些值存放到session域中,可是若是找不到對應的值,就會報異常,這裏能夠用@ModelAtrribute來進行修飾,對它更名就能夠了,代碼以下:
@RequestMapping("testModelAtrribute") public String testModelAtrribute(@ModelAttribute("abc") User user){ System.out.println("修改:"+user); return "spring"; }
或者加上一個有着@ModelAtrribute所修飾的方法,至於@ModelAtrribute註解,將會在第六章講到。
在咱們開發中,會有這樣一種狀況,好比說我要修改一我的的信息,可是用戶名是不讓修改的,那麼我在瀏覽器頁面中確定會有一個表單,裏面有一個隱藏域的id,能夠改密碼,能夠改郵箱,可是用戶名不讓修改,因此咱們不能給用戶名的輸入框,而後用戶修改完數據後,點擊提交保存,而後發現這個時候用戶名不見了!固然你們都會想到就是從新取出用戶名,而後給這個對象賦值,或者先從數據庫裏面找到這個用戶的對象,而後再來修改這個對象,而不是本身來建立對象,@ModelAtrribute註解就是基於這種狀況的。
那麼這種狀況在springMVC中要如何實現呢?
首先給出執行的目標方法的代碼:
@RequestMapping("testModelAtrribute") public String testModelAtrribute(User user){ System.out.println("修改:"+user); return "spring"; }
這個代碼很簡單,只是使用Pojo來獲取表單的參數,可是User類是不可能從表單獲得用戶名的,因此這個類就缺乏了一個屬性,若是這樣存到數據庫裏面,是確定要出問題的,那麼按照以前所說,咱們能夠先獲得這個User對象,而後給這個對象賦值,可是咱們有着簡化的方法,下面給出@ModelAtrribute註解的用法:
/** * 有@ModelAtrribute註解的方法,會在每一個目標方法執行前被springMVC調用 * @param id * @param map */ @ModelAttribute public void getUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){ if(id!=null){ System.out.println("每一個目錄方法調用前,都會先調用我!"); //模擬從數據庫中獲取對象 User user = new User(1L, "tom", "123456", "123456", "12"); System.out.println("從數據庫中獲取一個對象 "); map.put("user", user); } }
能夠發現,上面的類是沒有返回值的,可是經有一個map集合,咱們把這個從數據庫查出來的user對象放到了Map集合中,而後就不須要作什麼了,而後上面的
testModelAtrribute方法執行的時候,就會自動把用戶名給填充進去。
下面講一個@ModelAtrribute註解的執行流程
1.執行@ModelAtrribute註解修飾的方法:從數據庫中取出對象,並把對象放到了Map中,鍵爲user
2.springMVC從Map集合中取出User對象,並把表單的請求參數賦值給user對象相應的屬性
3.springMVC把上述對象傳入目標方法的參數
4.這個user對象是存在request中,若是jsp表單中有對應的字段,還會自動填充表單
注意:在@ModelAtrribute修飾的方法中,放入Map時的鍵要和目標方法的參數名一致
能夠一邊調試一邊查看源代碼,這裏的源代碼有點多,我就不貼出來了,有興趣的同窗能夠本身看,我這裏講訴一下原理:
1.調用@ModelAtrribute註解修飾的方法,其實是把@ModelAtrribute中的Map放到了implicitModel中。
2.解析請求處理器的標參數,實際上目標參數來自於WebDataBinder對象的target屬性
(1).建立WebDataBinder對象
>肯定objectName屬性,若傳入的attrName屬性爲"",則objectName爲類名第一個字母小寫
>注意:atrributeName,若目標方法的Pojo屬性使用了@ModelAtrribute註解來修飾,則atrributeName值即爲@ModelAtrribute的value屬性值
(2).肯定target屬性
>在implicitModel中查找atrrName對應的屬性值,若存在ok,若不存在,則驗證Hander,是否使用了@SessionAtrributes進行修飾,若使用了,則嘗試從session中獲取attrName所對應的屬性值,若session中沒有對應的值,則拋出異常
>若Hander沒有使用@SessionAtrributes進行修飾,或@SessionAtrributes中沒有使用value值指定的鍵和attrName相互匹配,則經過反射建立了Pojo對象,這個時候target就建立好了。
3.springMVC把表單的請求參數賦給了WebDataBinder的target屬性
4.springMVC會把WebDataBinder的attrName和target給到implicitModel
5.把WebDataBinder的target做爲參數傳遞給目標方法的入參
其實咱們能夠在目標方法裏面的參數中,定義一個@ModelAtrribute註解,並把其值指定爲@ModelAtrribute註解修飾的方法中的map的鍵,下面給出代碼示例:
@RequestMapping("testModelAtrribute") public String testModelAtrribute(@ModelAttribute("abc") User user){ System.out.println("修改:"+user); return "spring"; }
能夠看到,目標方法的參數中,有着@ModelAtrribute修飾,其value屬性爲"abc",再來看一下@ModelAtrribute所修飾的方法:
/** * 有@ModelAtrribute註解的方法,會在每一個目標方法執行前被springMVC調用 * @param id * @param map */ @ModelAttribute public void getUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){ if(id!=null){ System.out.println("每一個目錄方法調用前,都會先調用我!"); //模擬從數據庫中獲取對象 User user = new User(1L, "tom", "123456", "123456", "12"); System.out.println("從數據庫中獲取一個對象 "); map.put("abc", user); } }
在這裏就但是很顯示的看到map是存放了一個"abc"的鍵。
在springMVC的配置文件中使用<mvc:view-controller>標籤可使得url不須要進入handler處理方法,就能夠直接跳轉頁面,配置方法以下
<!-- 配置直接進行轉發的頁面,無須進入handler方法 --> <mvc:view-controller path="good" view-name="spring"/>
根據如上的配置,就能夠直接在瀏覽器中輸入http://localhost:8080/project/good,就能夠跳轉至success.jsp頁面,而無需進入handler處理方法,更不須要進行@RequestMapping映射。
可是若是僅僅是這樣的配置,會有一個很大的問題,就是以前所寫的handler處理方法所有都不能使用了,所有會進行報錯,那麼要怎麼解決呢?能夠在springMVC的配置文件中,寫一個下面的標籤,就不會有這樣的問題了:
<!-- 這個標籤在實際開發中,一般要進行配置 --> <mvc:annotation-driven/>
只要寫上這樣的一個標籤,那麼就能夠解決上面的問題,並且也不要寫任何參數。不過這個標籤具體有什麼用呢?後面會做介紹。
下面來說一下自定義視圖,使用它能夠很好的和jfreechar或excel整合,下面來具體說明。
首先新建一個視圖,新建一個類,繼承view接口,而後覆蓋裏面的方法,代碼以下:
@Component public class HelloView implements View { @Override public String getContentType() { //這個方法是設置返回的類型,根據本身的須要設置 return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { //這裏顯示要打印的內容 response.getWriter().print("hello view ,time:"+new Date()); } }
如上所示,寫一個類,繼承View接口,而後覆蓋裏面的方法,就能夠本身自定義視圖了,可是目前這個視圖尚未用,須要在springMVC的配置文件中進行配置,才能使用這個視圖,配置方法以下:
<!-- 視圖解析器 BeanNameViewResolver:使用視圖的名字來解析視圖 --> <!-- 定義property屬性來定義視力的優先級,order值越小,越優先,InternalResourceViewResolver視圖的order最高爲int最大值 --> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> <property name="order" value="10"></property> </bean>
只要在springMVC的配置文件中寫如上的配置,那麼這個視圖就可使用了,而後咱們寫一個handler處理方法,代碼以下:
@RequestMapping("testView") public String testView(){ System.out.println("hello view"); return "helloView"; }
而後的話,咱們輸入以下url,http://localhost:8080/project/testView,就不會進行helloView.jsp,由於配置的解析視圖的order值爲最高,也就表明着它的優先級是最低的,因此會先執行咱們自定義的視圖,那麼就會在瀏覽器中顯示以前視圖中向瀏覽器寫的數據。
上面所說的所有都是視圖的轉發,而不是重定向,此次我來說一下重定向是怎麼操做的。
只要字符串中以forward或者redirect開頭,那麼springMVC就會把它解析成關鍵字,而後進行相應的轉發,或者重定向操做,下面給出示例代碼:
/** * 測試視圖的重定向 * 只要在字符串中加了foword或者redirect開頭,springMVC就會把它解析成關鍵字,進行相應原轉發重定向操做 * @return */ @RequestMapping("testRedirect") public String testRedirect(){ return "redirect:/spring.jsp"; } /** * 測試視圖的轉發 * 只要在字符串中加了foword或者redirect開頭,springMVC就會把它解析成關鍵字,進行相應原轉發重定向操做 * @return */ @RequestMapping("testForward") public String testForward(){ return "forward:/spring.jsp"; }
上面就分別是重定向與轉發操做,其實不止java代碼,<mvc:view-controller>標籤中的返回視圖,也能夠加上redirect或者forward字符串,也會進行相應的操做。
form表單向後臺處理方法提交一個參數的時候,若是提交一個日期的數據,然後臺接收的數據類型則是Date類型,那麼springMVC確定沒法將其轉換成,由於springMVC不知道你傳的數據的格式是怎麼樣的,因此須要爲接收的字段指定日期的格式,使用@DateTimeFormat註解,使用方法以下:
使用前提:須要在springMVC-servlet.xml的配置文件中配置<mvc:annotation-driven/>,這個在開發中確定會配置的,由於它有好多做用,若是不配置,則下面代碼無效:
<mvc:annotation-driven/>
下面是目標方法的代碼:
@RequestMapping("dateFormat") public void initBinder(@RequestParam("date") @DateTimeFormat(pattern="yyyy-MM-dd") Date date){ System.out.println(date); }
上面就是在接收的參數前面加了一個@DateTimeFormat註解,註解中寫明pattern屬性,寫上日期的格式,而後在瀏覽器輸入:http://localhost:8080/project/dateFormat?date=19951029,這樣springMVC就能夠把這個字符串轉成Date日期了。
若是是使用Pojo,使用一個對象來接收參數,那麼也是同樣的,一樣是在字段的上方,加上一個@DateTimeFormat註解,以下:
public class User { private Long id; private String username; private String password; private String email; private String age; private Address address; @DateTimeFormat(pattern="yyyy-MM-dd") private Date birthday; ..省略get,set方法 }
除了日期的格式化,咱們可能還會遇到數字的格式化,好比會計人員做帳時,數字喜歡這樣寫 1,234,567.8 由於這樣簡單明瞭,可是若是由java直接來解析的話,確定是不行的,由於這根本就不是一個Float類型的數據,確定是要報錯的,那麼要如何呢?咱們可使用@NumberFormat()註解,這個註解的使用方式,和使用前提和8.1章節,日期的格式化是同樣的,請先查看8.1章節,再看本章。
和以前同樣,<mvc:annotation-driven/>是確定要配置的,不過這裏就不詳細說明了,下面給出執行方法的代碼:
@RequestMapping("numFormat") public String numFormat(@RequestParam("num") @NumberFormat(pattern="#,###,###.#") Float f){ System.out.println(f); return "spring"; }
其使用方法,實際上是和@DateTimeFormat是同樣的,可是這裏的參數有必要說明同樣,」#「是表明數字,那麼這個時候,就能夠對其進行解析了。若是你傳入的是一個正確的Float類型的數據,那麼它會被正確的接收這個數字,若是不是一個Float類型的數據,那麼springMVC會嘗試使用@NumberFoamat註解的參數來嘗試解析。
如輸入的是http://locathost:8080/project?num=123,那麼springMVC會解析成123.0,若是是http://locathost:8080/project?num=123.1,則會正常顯示成123.1,那若是是http://locathost:8080/project?num=1,234,567.8這種特殊的寫法,則也會正確的解析成1234567.8
數據的檢驗是使用Hibernate-validator的擴展註解,它是javaee6.0提出的JSR303的實現,使用它,能夠用註解對數據進行校驗,下面來講一下使用方法。
1.首先要導入jar包,這不是spring的jar包,須要下載Hibernate-validator的jar包,而後添加到項目工程中
2.其次是要在springMVC的配置文件中,即springMVC-servlet.xml中配置<mvc:annotation-driven/>,不須要寫其它的屬性:
<mvc:annotation-driven/>
3.而後在字段面前加上對應的校驗註解,下面以一個bean做一個簡單的例子:
public class User { private Long id; //用戶名不能爲空 @NotEmpty private String username; private String password; //必須是email格式 @Email private String email; private String age; private Address address; //生日必須是之前的時間,而不能是將來的時間 @Past @DateTimeFormat(pattern="yyyy-MM-dd") private Date birthday; ..固然,省去了get,set方法 }
這樣就會對使用了註解的字段進行校驗,而後這樣還不行,還須要指定目標執行方法,因此須要在執行方法上面也加上一個註解@Valid,這樣加了這個註解的執行方法就會校驗數據,下面給出目標方法的註解:
@RequestMapping("testPojo") public String testPojo(@Valid User user,BindingResult result){ //程序較驗的結果,以衣類型轉換的結果,都會存放在bindingResult對象中,能夠把這個參數定義到方法中使用 //當從這個對象中獲得的錯誤的個數大於0的時候進行操做 if(result.getErrorCount()>0){ //遍歷全部的錯誤,把錯誤字段和錯誤消息打印出來 for(FieldError error : result.getFieldErrors()){ System.out.println(error.getField()+" "+error.getDefaultMessage() ); } } System.out.println(user); return "spring"; }
只要在這個方法上面加上@Valid註解,而後這個執行方法就會校驗數據,校驗的結果可使用BindingResult對象顯示,這個對象會不只僅會保存數據校驗的結果,還會保存數據類型轉換的結果,因此均可以使用這個對象獲得相應的信息,顯示的方法以下面的代碼如示。
注意:須要校驗的bean對象與其綁定結果對象或錯誤對象是成對出現的,他們中間不容許聲明其它的參數
經過上面的例子咱們知道可使用註解來驗證數據類型,可是具體可使用哪些註解呢,下面給出說明:
@Null 被註釋的元素必須爲 null @NotNull 被註釋的元素必須不爲 null @AssertTrue 被註釋的元素必須爲 true @AssertFalse 被註釋的元素必須爲 false @Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值 @Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值 @DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值 @DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值 @Size(max, min) 被註釋的元素的大小必須在指定的範圍內 @Digits (integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內 @Past 被註釋的元素必須是一個過去的日期 @Future 被註釋的元素必須是一個未來的日期 @Pattern(value) 被註釋的元素必須符合指定的正則表達式
//-----------------下面是hibernate-valitor新增長的 @Email 被註釋的元素必須是電子郵箱地址 @Length 被註釋的字符串的大小必須在指定的範圍內 @NotEmpty 被註釋的字符串的必須非空 @Range 被註釋的元素必須在合適的範圍內
而在springMVC中,使用json很是的簡單,可是首先須要引進其它的一些jar包,那就是jackson,這是一個解析json的jar包,而後就能夠直接使用了,下面給出代碼示例:
/** * 打印json字符串 * @return */ @ResponseBody @RequestMapping("testjson") public List testJson(){ List list = new ArrayList(); list.add("good"); list.add("12"); list.add("dgdgd"); list.add("99999999999"); return list; }
如上如示,只要在執行方法的上面加上@ResponseBody註解,而後定義目標方法的返回值,其返回值能夠是任意集合,也能夠是任意對象,而後springMVC會自動將其轉換成json
springMVC也封裝了文件上傳,變的極其簡單,可是須要引入common-io.jar與common.upload.jar包,而後須要在spinrgMVC-serlvet.xml中做以下的配置:
<!-- 配置nultipartresolver,注意:id名必須這樣寫,否則會報錯 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"></property> <property name="maxInMemorySize" value="10240000"></property> </bean>
<!-- 配置nultipartresolver,注意:id名必須這樣寫,否則會報錯 --> <!-- defaultEncoding:表示用來解析request請求的默認編碼格式,當沒有指定的時候根據Servlet規範會使用默認值ISO-8859-1。當request本身指明瞭它的編碼格式的時候就會忽略這裏指定的defaultEncoding。 --> <!-- uploadTempDir:設置上傳文件時的臨時目錄,默認是Servlet容器的臨時目錄。 --> <!-- maxUploadSize:設置容許上傳的最大文件大小,以字節爲單位計算。當設爲-1時表示無限制,默認是-1。 --> <!-- maxInMemorySize:設置在文件上傳時容許寫到內存中的最大值,以字節爲單位計算,默認是10240。 -->
注意:id必須如上寫法,否則會報錯
而後給出處理方法的代碼:
@RequestMapping("testFileUpload") public String testFileUpload(@RequestParam(value="desc",required=false) String desc, @RequestParam(value="file",required=false) MultipartFile files[]) throws IOException{ for(MultipartFile file : files){ System.out.println(desc); System.out.println(file.getOriginalFilename());//獲得文件的原始名字 System.out.println(file.getName());//獲得文件的字段的名字」file InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream("d:/"+file.getOriginalFilename()); int len=0; byte[] buf =new byte[1024]; while((len=in.read(buf))!=-1){ out.write(buf); out.flush(); } out.close(); in.close(); } return "spring"; }
若是是多個文件上傳,則改成數組,若是是單個,方式也是同樣,與struts2的文件的上傳極其的相似。
編寫攔截器極其簡單,只要編寫一個類,實現HandlerInterceptor的方法,而後在springMVC的配置文件中配置這個類,就可使用這個攔截器了。
首先給出配置文件的寫法:
<!-- 配置自定義的攔截器 --> <mvc:interceptors> <!-- 這個bean就是自定義的一個類,攔截器 --> <bean class="zxj.intercepter.FirstInterceptor"></bean> </mvc:interceptors>
而後再來寫FirstInterceptor這個攔截器,代碼以下:
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * 寫一個攔截器,須要實現HandlerInterceptor接口 * @author jie * */ public class FirstInterceptor implements HandlerInterceptor { /** * 當目標方法執行以前,執行此方法,若是返回false,則不會執行目標方法,一樣的,後面的攔截器也不會起做用 * 能夠用來作權限,日誌等 */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("這個方法會最早執行.."); return true; } /** * 執行目標方法以後調用,可是在渲染視圖以前,就是轉向jsp頁面以前 * 能夠對請求域中的屬性,或者視圖進行修改 */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("執行目標方法以後調用,可是在渲染視圖以前,就是轉向jsp頁面以前"); } /** * 在渲染視圖以後被調用 * 釋放資源 */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("在渲染視圖以後被調用"); } }
而後在每一個執行方法調用以前,都會先進攔截器,這就是一個簡單的攔截器的寫法了。
在使用攔截器時候,並不必定要對全部的目標方法都進行攔截,因此咱們能夠只對指定的方法進行攔截,這就須要更改配置文件了,下面給出配置文件的寫法:
<!-- 配置自定義的攔截器 --> <mvc:interceptors> <!-- 這個bean就是自定義的一個類,攔截器 --> <bean class="zxj.intercepter.FirstInterceptor"></bean> <!-- 這個配置能夠配置攔截器做用(不做用)的路徑,不起做用的用<mvc:exclude-mapping path=""/> --> <mvc:interceptor> <!-- 這個path就是起做用的路徑,可使用通配符 --> <mvc:mapping path="/test*"/> <bean class="zxj.intercepter.SecondInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
只須要在<mvc:interceptors>中配置一個<mvc:interceptor>,而後指定其路徑,就能夠了,這個路徑能夠指定一個URL,也可使用通配符。
當同時定義了多個攔截器的時候,那麼它的使用順序是怎麼樣的呢?
preHandle是按配置文件中的順序執行的
postHandle是按配置文件中的倒序執行的
afterCompletion是按配置文件中的倒序執行的
如今來講一下springMVC的處理異常的方法,在目標方法中,能夠不處理異常,所有拋出去,交由springMVC來統一處理,並且處理方式要比struts2要簡單的多,下面則簡單的介紹一下怎麼用springMVC來處理異常。
首先定義一個會拋出異常的處理方法:
/** * 模擬一個異常,而後交由springMVC來處理 * @param i * @return */ @RequestMapping("byZero") public String byZero(@RequestParam("i") int i){ System.out.println("go."); System.out.println(10/i); return "spring"; }
看到上面的代碼,很顯示,若是瀏覽器提交出來的i是一個0的話,就會拋出一個算術的異常,由於0不能爲除數,拋出的異常爲ArithmeticException,若是不通過任何處理的話,異常就會往瀏覽器拋,而瀏覽器是沒法處理的,因此咱們要在springMVC中將其進行處理,下面就是處理方式的代碼,很簡單,只要定義一個方法,使有用@ExceptionHandler註解,代碼以下:
/** * 使用@ExceptionHandler註解,其value值是一個Class數組,且這個Class就是要拋出的那個異常的類,而後springMVC只要遇到這個異常,就會自行處理 * 參數中能夠定義一個Exception,裏面包含出錯的內容 * 注意:參數中不能定義Map,否則會報錯,因此若是要把異常存到request中,要用modelAndView * @param e * @return */ @ExceptionHandler({ArithmeticException.class}) public String hException(Exception e){ System.out.println(e); return "spring"; }
這就是springMVC的處理方式,首先來看一個@ExceptionHandler註解,它的value值是一個Class的數組,存放的就是要拋出的異常的類,好比除數爲0就會拋出ArithmeticException異常,那麼就在這個註解裏面的value值中聲明這個Class,那之後只要有出現這個異常,springMVC就會對其進行處理,固然若是以爲寫太多異常類太麻煩的話,能夠統一用Exception,由於它是全部異常類的父親類,因此它會處理全部的異常。
在這個異常的處理方法中,能夠聲明一個Exception參數,這個對象裏面封裝了引起異常的信息,能夠由這個對象查看是哪些異常出現。
不少人會想,獲得了Exception對象,而後直接把它存在request中就能夠了,可是若是在參數中聲明Map,是會報錯的。由於@ExceptionHandler修飾的方法中,參數不能有Map,既然不能使用Map往request中存值,那麼要如何解決呢?使用ModelAndView,能夠參照5.1章節的內容,下面給出示例代碼:
/** * 使用@ExceptionHandler註解,其value值是一個Class數組,且這個Class就是要拋出的那個異常的類,而後springMVC只要遇到這個異常,就會自行處理 * 參數中能夠定義一個Exception,裏面包含出錯的內容 * 注意:參數中不能定義Map,否則會報錯,因此若是要把異常存到request中,要用modelAndView * @param e * @return */ @ExceptionHandler({ArithmeticException.class}) public ModelAndView toException(Exception e){ ModelAndView mv = new ModelAndView(); mv.setViewName("error"); //雖然不能使用Map往request中存值,可是可使用下面的方法 mv.addObject("error", e); System.out.println(e); return mv; }
這樣就能夠往request中存放錯誤內容了。
異常是能夠聲明多個的,那麼若是同時聲明瞭多個異常,並且都能匹配上拋出的異常,那麼spirngMVC會怎麼處理呢?
假設同時定義了兩個異常,一個是ArithmeticException,還有一個是Exception,那麼確定會執行ArithmeticException,由於它的匹配度更高一點,或者說離這個異常的真實緣由更近一點,可是若是沒有定義ArithmeticException,那麼就會走Exception異常了。
細心的朋友也會發現,這上面定義的異常處理,都是隻能處理一個類的異常,那在實際開發中,確定會有着不少個處理類,那麼要怎麼定義一個全局的異常處理方法呢?
咱們能夠單獨寫一個類出來,使用@ControllerAdvice修飾這個類,而後再寫上異常的處理方法,而後它就是全局的一個異常處理了,代碼以下:
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; /** * 定義全局的異常處理
* 若是在處理方法的當前類找不到異常的處理方法,則會在全局找有着@ControllerAdvice修飾的類中的異常處理方法 * 只要使用@ControllerAdvice修飾這個類,而後再定義異常的處理方法,那麼它就是一個全局的異常處理了 * @author jie * */ @ControllerAdvice public class MyExceptionHandler { /** * 使用@ExceptionHandler註解,其value值是一個Class數組,且這個Class就是要拋出的那個異常的類,而後springMVC只要遇到這個異常,就會自行處理 * 參數中能夠定義一個Exception,裏面包含出錯的內容 * 注意:參數中不能定義Map,否則會報錯,因此若是要把異常存到request中,要用modelAndView * @param e * @return */ @ExceptionHandler({ArithmeticException.class}) public ModelAndView toException(Exception e){ ModelAndView mv = new ModelAndView(); System.out.println("gobal handler exception"); mv.setViewName("error"); //雖然不能使用Map往request中存值,可是可使用下面的方法 mv.addObject("error", e); System.out.println(e); return mv; } }
如上代碼能夠看到,方法體的內容徹底沒有變化,只是在類上面加了一個@ControllerAdvice,並且不須要在springMVC的配置文件中做任何的配置,就可使用這個全局的異常了。
注意:這個類裏面的異常的處理的優先級低於直接定義在處理方法的類中