容器
容器
參考資料:(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容器 的規範,就可以運行在各種不同的伺服器上。
如同
也如同 撰寫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 來處理。
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 來定義是比較麻煩,不過如果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格式包裝壓縮,而當中的結構如下
也就是說,一個 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() 方法來設定編碼了 例如:
當請求是用 GET 發送時,setCharacterEncoding() 沒有作用,究其原因,是因為處理 URI 的是 HTTP 伺服器,而非容器,對於 Tomcat 7 或先前版本附帶的 HTTP 伺服器來說,處理 URI 時使用的預設編碼是 ISO-8859-1,在不改變 Tomcat 附帶的 HTTP 伺服器 URI 編碼處理設定的情況下,常見使用底下的處理方式(若客戶端使用 UTF-8 發送請求):
從 Tomcat 8 之後,附帶的 HTTP 伺服器在 URI 編碼處理時預設使用 UTF-8 了,此時就不用如上自行轉換字串編碼了。
一旦開始學會從客戶端接受請求,取得請求參數或標頭等客戶端送出的資料之後,切記,永遠別相信你的客戶端是善良的,小心驗證、過濾請求參數或標頭等,以避免注入攻擊(Injection attack)之類的事情發生,永遠別把文件或書中簡單的範例程式直接拿來用,為了簡化範例,程式中往往不會考慮必要的驗證與過濾!
getReader()、getInputStream()
參考資料:(https://openhome.cc/Gossip/ServletJSP/GetReaderInputStream.html)
Last updated