> For the complete documentation index, see [llms.txt](https://brianwu.gitbook.io/brian/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://brianwu.gitbook.io/brian/java/servletjsp/rong-qi.md).

# 容器

## 容器

參考資料:(<https://openhome.cc/Gossip/ServletJSP/Container.html>)

### JAVA程式

.java -編譯-> .class .class 為 JVM的執行檔

### Servlet / JSP

#### 容器的概念

類似List、Set這類的Collection，不過容器的概念更廣泛，在最基本的功能上，不只持有物件，還負責物件的生命週期&相關服務的連結。

說穿了，容器也是一個JAVA程式，運行在JVM上，但是它會幫你把 Servlet/JSP 中的JAVA物件(HttpServletRequest、HttpServletResponse等)，轉換為HTTP那些文字性的通訊協定。

也可以想像成 Web容器 是一個伺服器，而我們寫的Servlet/JSP 就運行在上面。只要我們的 Servlet/JSP 符合 Web容器 的規範，就可以運行在各種不同的伺服器上。

如同

```
JVM     介於 Java程式    與 作業系統      之間
Web容器 介於 Servlet/JSP 與 實體Web伺服器 之間
```

也如同 撰寫Java 程式必須了解JVM/JRE 與我們的應用程式如何互動 撰寫Servlet/JSP 也必須知道容器如何與Servlet/JSP互動，如何管理Servlet (JSP最後也是轉譯、編譯、載入為Servlet，在容器的世界，真正負責請求、回應的是Servlet)

## 請求/回應的例子

1.客戶端(大部分是瀏覽器) 對Web 伺服器發出HTTP請求&#x20;

2.Web 伺服器收到HTTP請求，將請求轉由Web 容器處理，Web 容器會剖析HTTP 請求內容，建立各種物件(HttpServletRequest、HttpServletResponse、HttpSession等)&#x20;

3.Web 容器由請求的URL 來決定要使用哪個Servlet 來處理請求(事先由開發人員定義)&#x20;

4.Servlet 根據請求物件(HttpServletRequest) 的資訊決定如何處理，透過回應物件(HttpServletResponse)來建立回應。&#x20;

5.Web 容器與Web 伺服器溝通，Web 伺服器將回應轉換為HTTP 回應並傳回客戶端。

在Java EE 的領域無論是哪個技術，都與容器息息相關，寫Servlet/JSP 需要理解Web 容器，寫EJB 需要了解EJB 容器，寫應用程式客戶端 需要了解應用程式客戶端容器。

## 第一個Servlet

javax.servlet.http.HttpServletRequest 是 HTTP 請求的Java 物件，有關於HTTP 請求的資訊都是由它來取得。

javax.servlet.http.HttpServletResponse 則是HTTP回應的JAVA代表物件。

在Servlet 中從 response 取得PrintWriter，最後要不要呼叫close()呢? 一般是建議不需要主動呼叫close()，容器負責 HttpServletRequest、HttpServletResponse 物件的建立，也會處理最後的物件資源回收與銷毀，因此 close()可由容器處理(自行close() 在某些時候，像是過濾器(Filter) 的處理，可能會造成麻煩。

容器怎麼知道要由哪個Servlet 來處理請求? 在這個例子中，是透過標注 @WebServlet 來告知容器，這個Servlet 的名稱(name)是 HelloWorld，若沒有指定，預設採用類別的完整名稱。如果客戶端請求的URL是 /helloworld (urlPatterns)，則是由這個 Servlet 來處理。

```
@WebServlet(name="HelloWorld", urlPatterns={"/helloworld"},
loadOnStartup=1)
```

`http://localhost:8080/ServletDemo/helloworld`

應用程式啟動後，並不會載入所有的Servlet。容器會在請求時，才將對應的Servlet類別載入、實例化、進行初始動作所必須花費的時間，才真正得到請求的處理。

如果希望啟動時，就將Servlet類別載入、實例化、並做好初始化，則可以使用 loadOnStartup 設定。設定大於0 的值(默認為-1)，表示在啟動應用程式後就要初始化Servlet(而不是實例化)。數字表示 Servlet 的初始順序，容器必須保證有較小數字的 Servlet先初始化，如果有多個同個數字，則容器實作廠商可以自行決定要如何載入哪個Servlet。

像上方使用標注來定義Servlet 是Java EE 6 之後 Servlet 3.0 的功能。也可以使用 web.xml 來定義Servlet，就像在Java EE 5 中定義Servlet一樣。

```
<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="4.0" 
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"   
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">  
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>cc.openhome.HelloWorld</servlet-class>
         <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/helloworld</url-pattern>
    </servlet-mapping>
</web-app>
```

使用xml 來定義是比較麻煩，不過如果xml中`<servlet-name>` 和 標注的name相同，會把@WebServlet 的覆蓋掉。 可以使用標注當預設值，web.xml用來做日後更改設定值之用。

另外，有些Servlet 在定義時並不會使用 @WebServlet，像是使用框架時，會有擔任分派請求的Servlet，這類基本上會在web.xml中定義。

web.xml 也會用來定義一些應用程式的資源，像是初始參數、安全設定、JNDI(Java 命名與目錄介面)。

在上面的web.xml 中，若有客戶端請求 /helloworld，則是由 HelloWorld 這個Servlet來處理，這分別是由 `<servlet-mapping>` 中的 `<url-pattern>` 與 `<servlet-name>` 來定義，而 HelloWorld 名稱的Servlet，實際上是HelloWorld 類別的實例，這分別是由 `<servlet>` 中的 `<servlet-name>`與 `<servlet-class>`來定義。 如果有多個Servlet 在設定 `<load-on-startup>` 時使用了相同的數字，則依其在 web.xml 中設定的順序來初始 Servlet。

如果使用的是IDE，那基本上就可以執行應用程式並對 Servlet 發出請求了，實際上，IDE會將你的應用程式包裝為WAR (Web Archive)，然後上傳至應用程式伺服器(Application Server) 完成部署(Deployment)。

所謂的War檔，實際上是一個副檔名為 .war的檔案，使用zip格式包裝壓縮，而當中的結構如下

```
/ServletDemo.war/
                /WEB-INF/
                        /classes/
                        |       /cc/
                        |          /openhome/
                        |                   |HelloWorld.class
                        |web.xml
```

也就是說，一個 Web 應用程式中，編譯出來的 .class 檔案，必須放置在 /WEB-INF/classes/ 資料夾中，並必須按照套件（Package）層次放在對應的資料夾內，如果你使用 web.xml 設定檔，則 web.xm l必須放置在 /WEB-INF/ 中。 WAR 檔案是 zip 壓縮格式，所謂的部署，就是將 WAR 檔上傳至應用程式伺服器，由伺服器解壓縮並讀取設定、載入 Servlet 後進行服務。

## 第一個JSP

參考資料:(<https://openhome.cc/Gossip/ServletJSP/FirstJSP.html>)

## 關於 MVC/Model 2

參考資料:(<https://openhome.cc/Gossip/ServletJSP/Model2.html>)

## URL模式

1\. /XXXX/ */guest/*

/guest/home.view /guest/test.view

2\. *.XXXX* .view

所有.view 結尾的 3.

空字串是個特殊的URL，對應至環境根目錄，也就是 `/` 的請求 4. /

表示預設的Servlet。 5. 其餘的URL都是嚴格匹配(Exact match)

如果URL模式在設定比對的規則在某些URL有所重疊，會依照比對最嚴格的URL開始符合

## 只有 doGet()、doPost()？

參考資料:(<https://openhome.cc/Gossip/ServletJSP/DoGetDoPost.html>)

解釋各種 Method ，還有RESTful。

## 請求參數、標頭

參考資料:(<https://openhome.cc/Gossip/ServletJSP/RequestParameterHeader.html>)

`request.setCharacterEncoding("UTF-8");`

從 Servlet 4.0 開始，也可以在 web.xml 中加入 ，設定整個應用程式要使用的請求參數編碼，如此一來，就不用特別在每次請求使用 HttpServletRequest 的 setCharacterEncoding() 方法來設定編碼了 例如：

```
<request-character-encoding>UTF-8</request-character-encoding>
```

當請求是用 GET 發送時，setCharacterEncoding() 沒有作用，究其原因，是因為處理 URI 的是 HTTP 伺服器，而非容器，對於 Tomcat 7 或先前版本附帶的 HTTP 伺服器來說，處理 URI 時使用的預設編碼是 ISO-8859-1，在不改變 Tomcat 附帶的 HTTP 伺服器 URI 編碼處理設定的情況下，常見使用底下的處理方式（若客戶端使用 UTF-8 發送請求）：

```
String name = request.getParameter("name");
String name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
```

從 Tomcat 8 之後，附帶的 HTTP 伺服器在 URI 編碼處理時預設使用 UTF-8 了，此時就不用如上自行轉換字串編碼了。

**一旦開始學會從客戶端接受請求，取得請求參數或標頭等客戶端送出的資料之後，切記，永遠別相信你的客戶端是善良的，小心驗證、過濾請求參數或標頭等，以避免注入攻擊（Injection attack）之類的事情發生，永遠別把文件或書中簡單的範例程式直接拿來用，為了簡化範例，程式中往往不會考慮必要的驗證與過濾！**

## getReader()、getInputStream()

參考資料:(<https://openhome.cc/Gossip/ServletJSP/GetReaderInputStream.html>)
