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
-
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">
姓名 <input name="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就到了這裏了。
君子不行陌路,管它咫尺還是天涯
SpringMVC入門學習三