轉載自http://blog.csdn.net/justloveyou_/
摘要:
伴隨 J2EE 6一起發佈的Servlet 3.0規範是Servlet規範歷史上最重要的變革之一,它的許多新的特性都極大的簡化了 Java Web 應用的開發。本文從一個簡單的 Servlet 例子開始,說明了如何開發、配置一個 Servlet。此外,還重點敘述了Servlet的一些新特性,包括Servlet 異步處理、Servlet 非阻塞IO 以及 Servlet 文件上傳等內容,以便我們對Servlet有一個更全面的瞭解。
本篇主要介紹 Servlet 實踐方面的知識,更多關注於Servlet的新特性:
- Servlet 實例;
- Servlet 配置;
- Servlet 異步處理;
- Servlet 非阻塞IO;
- Servlet 文件上傳。
更多關於Servlet理論方面的介紹見我的上一篇博文《Servlet 綜述(理論篇)》,其具體包括以下幾個方面的內容:
- 爲什麼會有 Servlet;
- Servlet 是什麼;
- Servlet 如何實現預期效果;
- Servlet 的作用原理;
- Servlet 與 併發;
- Servlet 與 Java Web 應用的結構演變歷程;
- Servlet 與 MVC 的聯繫。
一. 從一個簡單的 Servlet 例子說起
我們看下面這個簡單的示例:
Servlet:
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//獲取請求參數
String param1 = request.getParameter("name");
String param2 = request.getParameter("gentle");
//獲取Servlet參數並放到request中
String age = this.getServletConfig().getInitParameter("age");
request.setAttribute("age",age);
// 此處進行業務邏輯處理
//根據處理結果轉發到相應的表現層進行顯示
request.getRequestDispatcher("/showInfo.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
web.xml 配置文件片段:
<context-param>
<param-name>campus</param-name>
<param-value>NEU</param-value>
</context-param>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.edu.tju.rico.servlet.TestServlet</servlet-class>
<init-param>
<param-name>age</param-name>
<param-value>24</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/servlet/test</url-pattern>
</servlet-mapping>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
顯示邏輯:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>showInfo</title>
</head>
<body>
請求參數: Name: <%= request.getParameter("name")%><br>
Gentle: <%= request.getParameter("gentle")%><br>
<br>
----------------我是分割線--------------------<br>
<br>
Web應用初始化參數: <%= application.getInitParameter("campus")%><br>
<br>
----------------我是分割線--------------------<br>
<br>
TestServlet 初始化參數: ${requestScope.age}<br>
<br>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
開發一個Servlet程序時,如果其是基於HTTP協議的,那麼我們一般繼承 HttPServlet 抽象類並重寫 doGet() 和 doPost() 方法,或者直接重寫 service() 方法去處理Http請求。
更多關於 JSP技術的細節見我的其他兩篇博客: 《Java Web基礎 — Jsp 綜述(上)》 和 《Java Web基礎 — Jsp 綜述(下)》。
二. Servlet 的配置
爲了讓 Servlet 能夠響應用戶請求,還必須將 Servlet 配置到我們的Web應用中。進一步地,如果我們沒有爲Servlet配置URL,那麼該Servlet將不能響應用戶請求。從 J2EE 6 (Servlet 3.0) 開始,配置 Servlet 的方式共有兩種:
-
在 web.xml 中進行配置;
-
在對應的Servlet類中使用@WebServlet註解進行配置。
我們在這裏主要說明使用@WebServlet註解進行配置Servlet,使用 web.xml 配置的方法與該種方式只是在形式不同,作用方式是一樣的,此不贅述。
一旦我們使用 @WebServlet 配置了Servlet,那我們就不用在 web.xml 進行再次配置了,並且不能在web.xml中將 metadata-complete 屬性設置爲true。 支持的常用屬性如下表所示:
屬性名 | 是否必需 | 類型 | 描述 |
---|---|---|---|
name | 否 | String | 指定 Servlet 的 name 屬性,等價於<servlet-name>標籤,默認取值爲Servlet類的全限定名 |
value | 否 | String[] | 該屬性等價於 urlPatterns 屬性,這兩個屬性不能同時使用 |
loadOnStartup | 否 | int | 指定Servlet的加載時機和順序,等價於<load-on-startup>標籤 |
initParam | 否 | WebInitParam[] | 指定一組 Servlet 初始化參數,等價於<init-param>標籤 |
asyncSupported | 否 | boolean | 聲明 Servlet 是否支持異步操作模式,等價於<async-supported>標籤 |
description | 否 | String | 該Servlet的描述信息,等價於<description>標籤 |
displayName | 否 | String | 該Servlet的顯示名,通常配合工具使用,等價於<display-name>標籤 |
將上述示例使用@WebServlet註解配置,如下:
@WebServlet(name = "Test",
urlPatterns = { "/servlet/test" },
initParams = { @WebInitParam(name = "age", value = "24") })
public class TestServlet extends HttpServlet {
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
三. Servlet的新特性:異步處理
1、Servlet 不支持異步處理會帶來哪些痛點?
在本篇的姊妹篇《Java Web基礎 — Servlet 綜述(實踐篇)》中,我們已經提到Servlet容器處理請求的方式。對於每個到達Web容器的請求,Web容器會爲其分配一條執行線程來專門負責該請求,直到迴應完成前,該執行線程都不會被釋放回Web容器的線程池。 我們知道,執行線程會耗用系統資源,若某些請求需要長時間處理(例如長時間運算、等待某個資源),就會長時間佔用執行線程,若這類的請求很多,許多執行線程都被長時間佔用,對於整個系統而言就會是個性能負擔,甚至造成應用的性能瓶頸。
特別地,基本上一些需長時間處理的請求,通常客戶端也較不在乎請求後要有立即的迴應,若可以,讓這類請求先釋放容器分配給該請求的執行線程,讓容器可以有機會將執行線程資源分配給其它的請求,這樣可以減輕系統負擔。這樣,原先釋放了容器所分配執行線程的請求,其迴應將被延後,直到處理完成(例如長時間運算完成、所需資源已獲得)再行對客戶端的迴應。
2、如何使用 Servlet 3.0 去支持對耗時事務的異步處理?
1)、Servlet 3.0 對異步處理的支持
在 Servlet 3.0 之前的規範中,如果Servlet作爲控制器調用了一個耗時的業務方法,那麼 Servlet 必須等到業務方法完全返回之後才能生成響應,這使得 Servlet 對業務方法的調用是一種阻塞式調用,因此效率比較低。Servlet 3.0 規範引入了異步處理來解決問題,異步處理允許Servlet重新發起一個線程去調用耗時的業務方法,這樣就可以避免等待。
Servlet 3.0 的異步處理是通過 AsyncContext 類來處理的,Servlet 可以通過 ServletRequest 的如下兩個方法開啓異步調用、創建 AsyncContext 對象。在這裏,AsyncContext 對象代表異步處理的上下文。
- AsyncContext startAsync()
- AsyncContext startAsync(ServletRequest,ServletRequest)
這兩個方法都會返回 AsyncContext 對象,前者會直接利用原有的請求與響應對象來創建AsyncContext對象,後者則允許你傳入自行創建的請求、響應對象。 在調用了startAsync()方法取得AsyncContext對象之後,這次的響應就會被延後,並釋放容器所分配的執行線程。
我們可以通過AsyncContext的getRequest()、 getResponse()方法取得請求、響應對象,此次對客戶端的響應將暫緩至調用AsyncContext的complete()方法或dispatch()方法爲止,前者表示響應完成,後者表示將調用指定URL對應的內容進行響應。特別需要注意的是,dispatch()前後仍是同一個請求,並且被異步請求dispatch的目標頁面必須指定:session=”false”。如果我們要支持 Servlet 的異步處理,我們的 Servlet 就必須能夠支持非同步處理。也就是說,如果我們使用@WebServlet來標註的話,則必須將其asyncSupported屬性設爲true,如下所示:
@WebServlet(urlPatterns = "/some.do", asyncSupported = true )
public class AsyncServlet extends HttpServlet {
...
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
特別需要注意的是,如果Servlet將支持非同步處理,並且其前端有過濾器,那麼過濾器也必須表明其支持非同步處理,如果使用@WebFilter註解的方式,同樣是需要設定其asyncSupported屬性爲true,如下所示:
@WebFilter(urlPatterns = "/some.do", asyncSupported = true )
public class AsyncFilter implements Filter{
...
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
2)、異步處理實例
(1) 進行異步處理的Servlet類:
@WebServlet(urlPatterns = "/async",asyncSupported=true )
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
request.setAttribute("param1", "我在異步處理前被設置...");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<title>異步調用示例</title>");
out.println("進入Servlet的時間:" + new java.util.Date() + ".<br/>");
//創建AsyncContext對象,開始異步調用
final AsyncContext async = request.startAsync(); // 在局部(匿名)內部類直接使用,必須設爲 final
// 設置異步調用的請求時長
async.setTimeout(10 * 1000);
// 啓動線程去處理耗時任務
async.start(new Runnable() { // 匿名內部類
@Override
public void run() {
try {
Thread.sleep(5000);
HttpServletRequest req = (HttpServletRequest) async
.getRequest();
req.setAttribute("param2", "我在耗時任務處理線程中被設置...");
async.dispatch("/async.jsp");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
out.println("結束Servlet的時間:" + new java.util.Date() + ".<br/>");
out.flush();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
(2) 表現層:
<%-- 被異步請求dispatch的目標頁面必須指定:session="false" --%>
<%@ page contentType="text/html; charset=utf-8" language="java" session="false"%>
<div style="background-color:#ffffdd;height:80px;">
param1:${param1}<br />
param2:${param2}<br />
<%
out.println("業務調用結束的時間:" + new java.util.Date());
%>
</div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
(3) 結果展示頁面
四、Servlet 3.1 支持非阻塞 IO
1、Servlet 不支持非阻塞 IO會帶來哪些痛點?
Servlet 3.0 允許異步請求處理,但僅限於傳統I/O,這大大限制了程序的可擴展性。我們知道,在應用程序中,一種典型的做法是,通過while循環讀取Servlet輸入流(Servlet InputStream),如下所示:
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
ServletInputStream input = request.getInputStream();
byte[] b = new byte[1024];
int len = -1;
while ((len = input.read(b)) != -1) {
. . .
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
事實上,Servlet 底層的 IO 是通過以下兩個 IO 流支撐的:
-
ServletInputStream:Servlet 用於讀取數據的輸入流;
-
ServletOutputStream:Servlet 用於輸出數據的輸出流。
以 Servlet 讀取數據爲例,傳統的讀取方式採用阻塞式IO —— 當Servlet讀取瀏覽器提交的數據時,如果數據暫時不可用,或者數據沒有讀取完成,Servlet當前所有線程將會被阻塞,無法繼續執行下去。另外,如果傳入的數據受到阻塞或流傳輸的速度慢於服務器讀取的速度,則服務器線程就需要一直等待數據的到來。同樣的情形在向Servlet輸出流(Servlet OutputStream)寫數據的時候也會出現。Servlet 3.1 提供的非阻塞IO進行輸入、輸出,可以更好地提升性能。
2、如何使用 Servlet 3.0 去支持非阻塞 IO?
上述問題可以通過添加Servlet 3.1(JSR 340,作爲Java EE7發佈的一部分)提供的事件監聽器ReadListener和WriteListener接口進行解決。通過 ServletInputStream.setReadListener 和 ServletOutputStream.setWriteListener 可以註冊監聽器。監聽器中提供了一些回調方法,在數據讀寫不受阻塞的時候進行觸發。以 ReadListener 爲例,實現ReadListener事件監聽器需要實現如下三個方法:
-
onDataAvailable():當有數據可用時激發該方法;
-
onAllDataRead():當所有數據讀取完成時激發該方法;
-
onError(Throwable t):讀取數據出現錯誤時激發該方法。
1)、Servlet 3.1 使用非阻塞IO步驟
在 Servlet 3.1 中使用非阻塞IO步驟可分爲三步:
(1) 調用 ServletRequest 的startAsync()方法開啓異步處理模式;
(2) 通過 ServletRequest 獲取 ServletInputStream,併爲 ServletInputStream 設置監聽器(ReadListener 實現類);
(3) 實現 ReadListener 接口來實現監聽器,在該監聽器的方法中以非阻塞方式讀取數據。
改進後的doGet方法如下所示:
AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));
- 1
- 2
- 3
- 1
- 2
- 3
setXXXListner方法指出採用非阻塞I/O而不是傳統I/O。onReadListener可以通過ServletInputStream進行註冊,同樣地,oneWritelistener可以通過ServletOutputStream進行註冊。特別地,新增加的ServletInputStream.isReady方法和ServletInputStream.isFinished方法用於檢測非阻塞I/O的讀取狀態,而ServletOutputStream.canWrite方法用於檢測數據是否能夠無阻塞地寫入。
2)、Servlet 3.1 使用非阻塞IO實例
(1) 請求提交表單 form.html:
<html>
<head>
<meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> </title>
</head>
<body>
<form action="asyn" method="post">
用戶名:<input type="text" name="name"/><br/>
密碼:<input type="text" name="pass"/><br/>
<input type="submit" value="提交">
<input type="reset" value="重設">
</form>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
(2) 在 Servlet中使用非阻塞IO:
@WebServlet(urlPatterns = "/async",asyncSupported=true )
public class AsyncServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
request.setAttribute("param1", "我在異步處理前被設置...");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<title>非阻塞IO示例</title>");
out.println("進入Servlet的時間:" + new java.util.Date() + ".<br/>");
//創建AsyncContext對象,開始異步調用
final AsyncContext async = request.startAsync();
// 設置異步調用的請求時長
async.setTimeout(10 * 1000);
final ServletInputStream input = request.getInputStream();
// 爲輸入流注冊監聽器
input.setReadListener(new ReadListener() {
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onDataAvailable() throws IOException {
System.out.println("數據可用!!");
try
{
// 暫停5秒,模擬讀取數據是一個耗時操作。
Thread.sleep(5000);
StringBuilder sb = new StringBuilder();
int len = -1;
byte[] buff = new byte[1024];
// 採用原始IO方式讀取瀏覽器向Servlet提交的數據
while (input.isReady() && (len = input.read(buff)) > 0)
{
String data = new String(buff , 0 , len);
sb.append(data);
}
System.out.println(sb);
// 將數據設置爲request範圍的屬性
async.getRequest().setAttribute("data" , sb.toString());
// 轉發到視圖頁面
async.dispatch("/asyn.jsp");
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
@Override
public void onAllDataRead() throws IOException {
System.out.println("數據讀取完成");
}
});
out.println("結束Servlet的時間:" + new java.util.Date() + ".<br/>");
out.flush();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
(3) 表現層:
<%@ page contentType="text/html; charset=utf-8" language="java" session="false"%>
<div style="background-color:#ffffdd;height:80px;">
瀏覽器提交數據爲:${data}<br/>
<%=new java.util.Date()%>
</div>
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
(4) 結果展示:
更多關於Servlet使用、實踐方面的介紹以及Servlet新特性的總結見我的下一篇博文《Servlet 綜述(實踐篇)》。
五、Servlet 3.0 支持文件上傳
Servlet 3.0之前的版本中,文件上傳是個挺讓人頭疼的問題,雖然有第三方框架(Apache Commons)來實現,但使用起來還是比較麻煩。在Servlet 3.0中,這些問題將不復存在,Servlet 3.0對文件上傳提供了直接支持,配合Servlet 3.0中基於Annotations的配置,大大簡化上傳件的操作。
在使用表單上傳文件時,我們需要使用@MultipartConfig註解去修飾對應的Servlet。此外,我們一方面需要在表單裏使用<input type=”file” …/>文件域,另一方面必須要爲表單域設置 enctype 屬性,其有三個值:
- application/x-www-form-urlencoded:表單數據被編碼爲名稱/值對,這是默認的編碼方式;
- multipart/form-data:以二進制流的方式處理表單數據,一般用於傳輸二進制文件,如圖片、視頻等;
- text/plain:不編碼特殊字符,適用於通過表單發送郵件。
下面跟進這個例子來體會其給我們帶來的便捷。
(1) 文件提交表單:
<%@ page contentType="text/html; charset=utf-8" language="java" errorPage="" %>
<head>
<title> 文件上傳 </title>
</head>
<body>
<form method="post" action="upload" enctype="multipart/form-data">
文件名:<input type="text" id="name" name="name" /><br/>
選擇文件:<input type="file" id="file" name="file" /><br/>
<input type="submit" value="上傳" /><br/>
</form>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
(2) 文件上傳Servlet:
@WebServlet(name="upload" , urlPatterns={"/upload"})
@MultipartConfig
public class UploadServlet extends HttpServlet
{
public void service(HttpServletRequest request ,
HttpServletResponse response)
throws IOException , ServletException
{
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
request.setCharacterEncoding("utf-8");
// 獲取普通請求參數
String name = request.getParameter("name");
out.println("普通的name參數爲:" + name + "<br/>");
// 獲取文件上傳域
Part part = request.getPart("file");
// 獲取上傳文件的文件類型
out.println("上傳文件的的類型爲:"
+ part.getContentType() + "<br/>");
//獲取上傳文件的大小。
out.println("上傳文件的的大小爲:" + part.getSize() + "<br/>");
// 獲取該文件上傳域的Header Name
Collection<String> headerNames = part.getHeaderNames();
// 遍歷文件上傳域的Header Name、Value
for (String headerName : headerNames)
{
out.println(headerName + "--->"
+ part.getHeader(headerName) + "<br/>");
}
// 獲取包含原始文件名的字符串
String fileNameInfo = part.getHeader("content-disposition");
// 提取上傳文件的原始文件名
String fileName = fileNameInfo.substring(
fileNameInfo.indexOf("filename=\"") + 10 , fileNameInfo.length() - 1);
// 將上傳的文件寫入服務器
part.write(getServletContext().getRealPath("/uploadFiles")
+ "/" + fileName ); // ①
System.out.println(getServletContext().getRealPath("/uploadFiles"));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
(3) 結果展示:
六、拾遺增補
除上面提到的內容,Servlet 還引入了其他新的特性(如下所述),此不贅述。
- Servlet 3.0 爲 Web 模塊化提供了支持;
- Servlet 3.1 可以強制更改 Session ID,具體由 HttpServletRequest 的 changeSessionId()方法完成。