SpringMVC入門學習三
今天是Springmvc學習的第三天,今天我將主要介紹一下:
- 常用註解的使用
- 關於非post、get請求的處理
- 檔案上傳與下載
- 攔截器
常用註解的使用
老大在此
@Controller
@Controller放在類的上面,用於將一個類標記為Controller,處理DispatcherServlet分發的請求,當然真正處理請求的還是使用@RequestMapping註解的方法。
處理request URI的註解
@RequestMapping
@RequestMapping是一個用來處理請求地址對映的註解,可用於類上面【代表模組】和方法上面【代表業務】
裡面 引數屬性
,其中所有的引數都可以傳入 陣列
的形式:
-
value:代表請求的地址如"/login",可以寫多個【使用陣列】,只寫它時,可以省略不寫
-
name:value的別名,作用是一樣的。
-
method:請求的method型別,裡面要傳入
RequestMethod
型別的資料,如GET,POST,PUT等等,可以指令多個(使用陣列傳遞即可)它的衍生品:
- @GetMapping:專門處理get請求
- @PostMapping:專門處理Post請求
-
params:設定需要的引數,如果沒有,請求就過不去,還可以設定引數是否等於或則不等於
// 當穿過來的引數data不為not才進行響應 @RequestMapping(value = "/test1",params = "data!=not") // 當穿過來的引數data為hava才進行響應 @RequestMapping(value = "/test1",params = "data=have")
ps:中間注意
別打空格
-
headers
指定request中必須包含某些指定的header值,該方法才會處理請求。
@RequestMapping(value="/hello",headers="Referer=https://baidu.com")
-
consumes 消費者,指令請求提交內容的型別(Content-Type),例如可以限令必須為application/json;charset=UTF-8,指令多個使用陣列形式
-
produces 生產者,指令放回的內容的型別,但是必須是請求頭所包含的型別
@PathVariable
這個是restful風格的請求方式,用於將URL中的模板變數對映到方法的引數上面,這個在我的上一篇部落格有所介紹。
共享資料
@ModelAttribute
@ModelAttribute註解可以被應用在方法引數上面和方法宣告上面。其中被@ModelAttribute註解的方法會優先於controller方法(被@RequestMapping註解)之前執行,因為模型物件優先於controller方法建立。
簡單的來說,@ModelAttribute有兩個作用, 將資料儲存在model上面
【標註在方法上面】,將資料從 model取出來
【標註在引數裡面】
將資料儲存在model上面
-
使用@ModelAttribute註解無返回值方法
@ModelAttribute public void model1(Model model){ model.addAttribute("modle1","你好呀"); } @RequestMapping("/modleC") public String modleC(){ return "test"; }
其實這個就相當於
@RequestMapping("/modleC") public String modleC(Model model){ model.addAttribute("modle1","你好呀"); return "test"; }
-
註解有返回值的方法
/** * 註解有返回值的 * 在這種情況下,放回值物件會預設的放在隱藏的Model中。 * 其key為放回值 **型別** 首字母小寫,當然也可以在@ModelAttribute * 加上value或則name的屬性來指令key * @return */ @ModelAttribute public String modeler02(){ String Hello = "你好呀"; return Hello; //在這裡面key為string }
-
與@RequestParam結合使用
@RequestParam的具體用法將在下面介紹
加入此時我們傳送了一個這樣的請求:
/test/name=googboy
// 取出get請求的引數name,將其存入modle中 @ModelAttribute("name") public String modeler02(@RequestParam()String name){ return name; } @RequestMapping("/test") public String test(){ return "test"; }
將資料從model取出來
假如此時有了一個model, key為name,value為Tom
資料,那麼我們可以通過@ModelAttribute將資料取出來。
@RequestMapping("/test") public String test(@ModelAttribute("name")String name){ // 輸出是名字是Tom System.out.println("名字是"+name); return "test"; }
儲存資料
@SessionAttributes
假如希望在多個請求之間共用資料,則可以在控制器類上面標註一個@SessionAttributes,其中value指令是model中的哪一個存入session,type為資料型別,兩者都可以放陣列型別的資料。
取出資料
@SessionAttribute
@SessionAttribute的作用很簡單,就是獲得session的值
假如此時有一個session key為name,value為Tom
,獲得session
@RequestMapping("/test2") public String test2(@SessionAttribute("name") String name){ System.out.println("session結果是"+name); return "test"; }
假如此時不存在就會報錯,這是可以使用required來確定是否必須
@RequestMapping("/test2") //非必須,同時value/name必須加上 public String test2(@SessionAttribute(value = "name",required = false) String name){ System.out.println("session結果是"+name); return "test"; }
對於取出引數的處理
@RequestParam
@RequestParam主要是用於在Springmvc後臺控制層獲得引數,類似request.getParameter("name")
它有下列引數
-
value:value表示要取出的引數,當只有value這個引數時,可以省略不寫。
-
name:value的別名,作用和value一樣。
-
required:預設值是true,當傳入的引數值不存在時,程式就會報錯,這時候就可以將required設定為false。
-
defaultValue:當設定defaultValue時,會自動的將required設定為false,如果此時請求引數不存在,就會預設的將引數設定為defaultValue。當然,如果引數存在,defaultValue也就是不會發揮作用了。
舉個栗子:
@RequestMapping("/test3") public String test3(@RequestParam(value = "name",defaultValue = "TomCat") String name){ // 請求網址是: /test3 // 輸出是名字是TomCat System.out.println("名字是"+name); return "test"; }
@RequestBody
這時候大家可能會問,既然有@RequestParam來處理方式了,為什麼我們還要使用@RequestBody來獲取引數呢?
這個主要主要是因為他們處理資料的型別 Content-Type
不一樣
-
@RequestParam:處理application/x-www-form-urlencoded編碼的內容,提交方式為Get,Post。
-
@RequestBody:通常是來處理非
application/x-www-form-urlencoded編碼格式
的內容的資料,比如說application/json
和application/xml
。
既然是處理json資料,那麼就需要使用json的jar包。
<!--====>jackson包匯入start--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.7</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.7</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.7</version> </dependency> <!--====>jackson包匯入end-->
當發了一個如下所示的json請求時
$(document).ready(function () { $("button").click(function () { $.ajax({ url: "/test4", type:"post", data:JSON.stringify({ "name":"TODO" }), dataType:"json", contentType:"application/json; charset=utf-8", success:function (data) { console.log(data) } }); }); });
controller程式碼,在引數裡面加上@RequestBody:
@RequestMapping("/test4") public String test4(@RequestBody String name){ // 名字是{"name":"TOFO"} System.out.println("名字是"+name); return "test"; }
對於返回資料的處理
@ResposeBody
返回json資料
在大多數的情況下,我們不一定是想返回一個檢視,只想返回一個json資料或者說xml資料,那麼這時候我們就要使用@RequestBody了。@ResponseBody註解被應用於方法上,標誌著響應會寫到響應體裡面去,而不是放到檢視Model裡面去。
那麼就可以這樣使用:
@RequestMapping(value = "/test4") @ResponseBody public User test4(@RequestBody String name){ // 名字是{"name":"TOFO"} System.out.println("名字是"+name); User u = new User(); u.setName("小明"); u.setAge("17"); return u; }
返回xml資料
要匯入額外的jacksonxml jar包
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.9.7</version> </dependency>
// 由於要返回xml格式的資料,所以需要設定produces為application/xml // 使用MediaType裡面的靜態常量,可以防止寫錯 @ResponseBody @RequestMapping(value = "/test5",produces = org.springframework.http.MediaType.APPLICATION_XML_VALUE) public User test5(){ User u = new User(); u.setName("小紅"); u.setAge("18"); return u; }
SpringMVC關於非post、get請求的處理
在HTML5的規範中,表單元素唯二允許的HTTP的方法是GET和POST,但是如果我們想使用PUT或則DELET方法,那怎麼辦呢?
- 在瀏覽器中裝作自己是post或則get請求,實際上是PUT請求
<!-- 裝成自己是post請求 --> <form action="/put" method="post"> <!-- 實際上想傳送的PUT請求 --> <input type="hidden" name="_method" value="PUT"> 姓名 <inputname="name" type="text"> <input type="submit" value="提交"> </form>
- 在web.xml檔案中,從
_method
找到真正的http請求
加入
<filter> <!-- 原理就是從_method找到真正想要http請求,然後分發給controller 所以這個必須寫在DispatcherServlet的前面 --> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <servlet-name>springmvc</servlet-name> </filter-mapping> <!--註冊一個前端控制器--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!--上下文配置的位置的制定--> <param-name>contextConfigLocation</param-name> <!--此時是在類路徑下面去尋找--> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <!--servlet的對映配置--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!--這裡統一寫/--> <url-pattern>/</url-pattern> </servlet-mapping>
檔案上傳與下載
檔案上傳
檔案上傳同時也是使用form表單進行提交,一般我們的表單的提交是文字型的資料,實際上就是進行字串的拼接。那麼檔案便不是這樣的進行處理,這時候我們就要使用到multipart表單了。
首先我們的配置一個MultipartResolver來告訴DispatchServlet如何來處理multipart請求。
在Servlet3.0中,Spring會預設註冊一個StandardServletMultipartResolver,我們只需要在web.xml啟用 <multipart-config>
就行。
<multipart-config> <location>/tmp</location> </multipart-config>
屬性 | 作用 |
---|---|
location | 上傳檔案所存放的臨時目錄。必須指定 |
max-file-size | 檔案的最大大小,單位為位元組。預設沒有限制 |
max-request-size | 請求的最大大小,單位為位元組。預設沒有限制 |
file-size-threshold | 檔案大小閾值,當大於這個閾值時將寫入到磁碟,否則在記憶體中。預設值為0 |
前端程式碼
<form action="/upload" method="post" enctype="multipart/form-data"> 檔案 <input type="file" name="file"/> <input type="submit" value="提交"> </form>
控制檔案上傳的程式碼
@RequestMapping("/upload") public String upload(@RequestParam("file")MultipartFile file, HttpServletRequest request) { // 假如沒有檔案 if (!file.isEmpty()) { // 獲得檔案原始名字 String fileName = file.getOriginalFilename(); // 獲得檔案的字尾名 String fileSuffix = fileName.substring(fileName.lastIndexOf(".")); // 取新的名字 UUID可以保證產生的名字不一樣,產生全球唯一的ID String newName = UUID.randomUUID() + fileSuffix; // 獲取檔案儲存目錄地址 String filePath = request.getSession().getServletContext().getRealPath("/upload"); File fileForPath = new File(filePath); // 如果檔案不存在 if (!fileForPath.exists()) { // 新建檔案 fileForPath.mkdir(); } File targetFile = new File(filePath, newName); try { // 將檔案寫入 file.transferTo(targetFile); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("檔案不存在"); } return "test"; }
檔案下載
@RequestMapping("down") public String down(HttpServletResponse response,HttpServletRequest request){ // 假如我的檔案在/home/xiaohiu/images/鬼刀.jpg String fileName= "鬼刀.jpg"; String parentPath = "/home/xiaohiu/images"; // 這時候path.toString就是/home/xiaohiu/images/鬼刀.jpg了 Path path = Paths.get(parentPath,fileName); // 假如檔案存在 if (Files.exists(path)){ // 取出字尾名 jpg String fileSuffix = fileName.substring(fileName.lastIndexOf(".")+1); // 設定ContentType,只有設定它,才會去下載 response.setContentType("application/"+fileSuffix); try { // 新增頭資訊 同時對檔名重新編碼防止中文名亂碼 response.addHeader("Content-Disposition","attachment;filename="+new String(fileName.getBytes("utf-8"),"ISO8859-1")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } try { // 使用輸出流寫出去 Files.copy(path,response.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } }else { System.out.println("檔案不存在"); } return "test"; }
SpringMVC攔截器
攔截器實在我們請求之前做檢查,同時有權決定我們接下來是否繼續,同時也可以對我們的請求進行加工處理,可以設定多個攔截器。
看到這裡,是不是想到了我在前面介紹的 AOP
,其實我們可以理解為 Spring攔截器是
SpringMVC對AOP的一種實現方式。AOP是通過配置 <aop:config>
進行配置,而攔截器是通過 <mvc:interceptors>
進行配置。
那麼我們怎麼實現呢?
-
實現HandlerInterceptorAdapter介面
在SpringMVC中,為實現攔截器功能,有兩種方式,一個是實現
HandlerInterceptor
介面,第二個是實現WebRequestInterceptor
介面,這裡我們選擇使用HandlerInterceptor。在HandlerInterceptor介面中,定義了三個方法
-
preHandle():請求之前呼叫,返回
true
或則false
,返回true,接下來的postHandle和afterCompletion才會起作用。 -
postHandle():在請求之後呼叫,也就是controller方法執行完後再呼叫,但是卻是在DispatcherServlet進行
檢視返回渲染之前
被呼叫。也就是說,這個可以對modle進行操作。 -
afterCompletion():在DispatcherServlet進行完試圖渲染之後才執行。
public class TestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("請求前進行攔截"); // 返回true才會查詢下一個攔截器,如果沒有下一個攔截器,則返回controller return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("檢視渲染前進行攔截"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("檢視渲染後進行攔截"); } }
-
-
攔截器的配置
在springmvc的xml配置檔案中
<!--配置攔截器--> <mvc:interceptors> <!--配置一個攔截器 可以配置多個,那麼攔截順序則是從上到下 --> <mvc:interceptor> <!--攔截的URI /* 代表攔截這一層的任意字元 /** 代表攔截任意層次的任意字元 --> <mvc:mapping path="/*"/> <!--不進行攔截的uri--> <mvc:exclude-mapping path="/get"/> <bean class="cc.weno.interceptor.TestInterceptor"/> </mvc:interceptor> </mvc:interceptors>
這時候就可以進行攔截了。
-
攔截器順序的問題
攔截器順序是根據配置的順序來決定的,但是pre、post、after卻有些區別,這張圖就可以表示了。
好了,今天的Springmvc就到了這裡了。
君子不行陌路,管它咫尺還是天涯