容器

容器

參考資料:(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請求

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

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

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

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)

Last updated