Spring MVC 使用原生 Servlet
1. 前言
本節(jié)課程和大家一起聊聊在 Spring MVC 中如何使用原生 Servlet API。通過(guò)本節(jié)課程,你將學(xué)習(xí)到:
- 使用 Servlet API 作為控制器方法的入?yún)ⅲ?/li>
- 使用 Spring MVC 的 Servlet API 代理類(lèi)作為控制器方法的入?yún)ⅲ?/li>
- 使用 IO 流對(duì)象作為控制器方法的入?yún)ⅰ?/li>
本章節(jié)重點(diǎn)在于理解 Spring MVC 高級(jí)組件與低級(jí)組件之間的關(guān)系。
2. Servlet API
使用 Spring MVC 框架,可以給開(kāi)發(fā)者帶來(lái)諸多便利性,如自動(dòng)綁定請(qǐng)求包中的數(shù)據(jù)、自動(dòng)數(shù)據(jù)驗(yàn)證……
在 Spring MVC 構(gòu)建的 WEB 項(xiàng)目中,其實(shí)也可以直接使用原生的 Servlet API 。但在真實(shí)項(xiàng)目中,還是少用為好,畢竟項(xiàng)目是有工期的。
快速迭代是現(xiàn)代項(xiàng)目開(kāi)發(fā)所追求的目標(biāo),這也是開(kāi)發(fā)過(guò)程中選擇框架的目的。但是,了解本章節(jié)的知識(shí),對(duì)于更好的理解 Spring MVC 有很大的幫助。
先一起回憶幾個(gè)常用的 Srevlet API:
- HttpServletRequest: 用來(lái)處理請(qǐng)求包;
- HttpServletResponse: 用來(lái)處理響應(yīng)包;
- HttpSession: 會(huì)話對(duì)象;
- ServletConfig: 配置對(duì)象;
- ServletContext: Servlet 上下文對(duì)象。
這幾個(gè) API 應(yīng)該很熟悉。因?yàn)?Spring MVC 對(duì)原生 Servlet API 進(jìn)行了高級(jí)封裝,使用 Spring MVC 開(kāi)發(fā)時(shí),開(kāi)發(fā)者不需要直接使用原生 API ,但并不意味著我們不再需要它們,只是換了一種方式使用而已,由 Spring MVC 內(nèi)部組件使用它們。
Spring MVC 雖然封裝了底層 Servlet API 的使用,但并沒(méi)有切斷開(kāi)發(fā)者直接對(duì)原生 Servlet API 的調(diào)用之路。對(duì)于需要使用原生 Servlet API 的場(chǎng)景,Spring MVC 提供了各種方便。
只需要在控制器的方法中聲明需要使用的原生 API 類(lèi)型做參數(shù)即可。
@RequestMapping("/testApi01")
public String hello(HttpServletRequest request,HttpServletResponse response) {
return "success";
}
這一切都應(yīng)該要感謝 Spring 強(qiáng)大的依賴(lài)注入功能。可以說(shuō),在 Spring 的世界里,你想要什么就有什么。調(diào)用上面方法時(shí),Spring 就會(huì)注入你所需要的 API 對(duì)象。至于在方法中如何使用它,相信你一定已經(jīng)游刃有余。
如果你需要使用更多的原生 API。如上面代碼一樣,在方法參數(shù)中告訴 Spring 你所需要的 API 類(lèi)型就可以了。
@RequestMapping("/testApi02")
public String hello(HttpSession session) {
return "success";
}
3. Servlet API 代理類(lèi)
什么是 Servlet API 代理類(lèi)?
Spring MVC 在它的 org.springframework.web.context.request 包中提供的有 WebRequest 接口,此接口可以用來(lái)代理原生 HttpServletRequest 的功能。
其本質(zhì)就是給原生 HttpServletRequest 加了一個(gè)外殼,擴(kuò)充了點(diǎn)功能,實(shí)質(zhì)還是在使用 HttpServletRequest 的功能。這個(gè)接口或者說(shuō)是與此類(lèi)似的接口,大家可以作為常識(shí)性知識(shí)了解,在企業(yè)開(kāi)發(fā)過(guò)程并不常用。
和使用原生 Servlet API 一樣,只需要在控制器的方法中聲明成參數(shù)便可。
@RequestMapping(value = "/testApi03")
public String hello(WebRequest request) {
String userName = request.getParameter("userName");
return "success";
}
4. 使用 IO 流
WEB 程序整體上是 B / S 結(jié)構(gòu),本質(zhì)上是基于底層網(wǎng)絡(luò)的程序,是 C / S 結(jié)構(gòu)體系的升級(jí)?;貞浺幌戮W(wǎng)絡(luò)編程的幾個(gè)基本要素:
- 服務(wù)器端建立監(jiān)聽(tīng)端口;
- 客戶(hù)端向監(jiān)聽(tīng)端口發(fā)起網(wǎng)絡(luò)連接;
- 連接成功后建立起雙向的網(wǎng)絡(luò)流傳輸通道;
- 彼此之間通過(guò)網(wǎng)絡(luò)流通道進(jìn)行數(shù)據(jù)交流。
程序的本質(zhì)就是解決數(shù)據(jù)從哪里來(lái)、如何處理數(shù)據(jù)以及數(shù)據(jù)要去哪里的問(wèn)題。基于網(wǎng)絡(luò)的程序也是如此,其數(shù)據(jù)的來(lái)來(lái)往往是通過(guò) IO 流實(shí)現(xiàn)的。
WEB 程序也是網(wǎng)絡(luò)程序,數(shù)據(jù)的來(lái)去也是通過(guò) IO 流實(shí)現(xiàn)??赡苣銜?huì)說(shuō),使用原生 Servlet 規(guī)范開(kāi)發(fā) WEB 項(xiàng)目時(shí),你從沒(méi)有接觸或使用過(guò)這樣的流、哪樣的流。
那是因?yàn)闊o(wú)論你使用原生 Servlet API 還是使用 Spring MVC 開(kāi)發(fā) WEB ,兩者已經(jīng)幫你隱藏了其中的細(xì)節(jié)。
HttpServletRequest 其本質(zhì)就是封裝網(wǎng)絡(luò)輸入流的操作。如果你不信,你可以查看 HttpServletRequest 的相關(guān)方法,會(huì)看到:
InputStream inStream=request.getInputStream();
觸類(lèi)旁通,此時(shí)你應(yīng)該能理解到 HttpServletResponse 封裝了網(wǎng)絡(luò)輸出流的相關(guān)操作。
OutputStream outputStream= response.getOutputStream();
那么,在 Spring MVC 中我們能不能在控制器的方法中直接引用底層的 IO 流對(duì)象?
當(dāng)然可以!
TIps: Spring MVC 既可以把原生的 Servlet API 對(duì)象注入到控制器的方法中,也可以把更底層的 IO 流對(duì)象注入到控制器方法中。
4.1 注入 InputStream
在控制器的方法中聲明 InputStream 作為入?yún)ⅲ?strong>Spring MVC 就能注入你想要的 InputStream 對(duì)象。
@RequestMapping(value = "/testApi04",method = RequestMethod.POST)
public void hello(InputStream inputStream) throws IOException {
byte[] buff=new byte[128];
int read= inputStream.read(buff);
System.out.println(new String(buff,0,read));
}
上面的實(shí)例,能讀取到請(qǐng)求包中的數(shù)據(jù),但過(guò)于低級(jí),可讀性并不是很好。
Tips: 控制器方法的映射機(jī)制有只接受 POST 方法的限制,如果是 GET 方法的請(qǐng)求包,直接使用 InputStream 對(duì)象無(wú)法獲取到請(qǐng)求包中的數(shù)據(jù)。
GET 方法的請(qǐng)求數(shù)據(jù)是附加在 URL 上的,InputStream 只能讀取實(shí)體部分的數(shù)據(jù)。
4.2 注入 OutputStream
在控制器的方法中注入 OutputStream 對(duì)象,只需要在方法中添加參數(shù)聲明。
如下實(shí)例:可使用 OutputStream 對(duì)象讀取指定文件中的內(nèi)容后直接響應(yīng)給瀏覽器。
@RequestMapping(value = "/testApi05")
public void hello(OutputStream outputStream) throws IOException {
Resource res = new ClassPathResource("/test.txt");
FileCopyUtils.copy(res.getInputStream(), outputStream);
}
test.txt 文件的內(nèi)容是”this is a test’。文件直接放在項(xiàng)目的 src/main/java 目錄下。
在瀏覽器中輸入請(qǐng)求路徑 http://localhost:8888/sm-demo/testApi05 。你將在瀏覽器中看到:
有句話叫做 “條條道路通羅馬”,用在 Spring MVC 中真的是合適,依靠 Spring 強(qiáng)大的注入功能,只要原生開(kāi)發(fā)中能有的對(duì)象基本上都能注入進(jìn)去。
5. 小結(jié)
本節(jié)課程和大家講解了在控制器方法中如何直接使用原生 Servlet API 和 更底層的 IO 流對(duì)象。
使用起來(lái)不難。但通過(guò)本節(jié)課程,希望大家能更透徹地理解 Spring MVC 和原生 Servlet API、網(wǎng)絡(luò)編程之間的關(guān)系。
-
Servlet API 是 J2EE 擬定的 企業(yè)級(jí) WEB 應(yīng)用程序開(kāi)發(fā)規(guī)范,可以認(rèn)為是官方提出來(lái)的開(kāi)發(fā)框架。對(duì) WEB 開(kāi)發(fā)過(guò)程中的更低級(jí)的網(wǎng)絡(luò)流操作進(jìn)行了封裝;
-
Spring MVC 則是對(duì)原生 Servlet API 進(jìn)一步進(jìn)行了封裝。一切只是為了讓開(kāi)發(fā)過(guò)程更高效。