Weitere ähnliche Inhalte Ähnlich wie 深入淺出 Web 容器 - Tomcat 原始碼分析 (20) 深入淺出 Web 容器 - Tomcat 原始碼分析1. 深入淺出 Web 容器
Tomcat 原始碼分析
林信良
資深技術顧問
http://openhome.cc
caterpillar@openhome.cc
2. 主題
• Web 容器與 Servlet
• 從 HTTP 請求到 service()
• 在service()的前後
• 從 JSP 到 Servlet
• 自訂標籤處理
5. Web 容器與 Servlet
管很多事?哪些事啊?
你沒想過 HTTP 那些麻煩的
東西是怎麼變成 Java 物件的
嗎?你以為 Servlet 中的
Request 與 Response 是怎
麼來的?
9. RequestFacade requestFacade =
if(servlets.get(servletName) == null) {
new RequestFacade(request);
servlet = (Servlet) myClass.newInstance();
ResponseFacade responseFacade =
servlets.put(servletName, servlet);
new ResponseFacade(response);
servlet.init(new Config(myClass));
}
else {
servlet = servlets.get(servletName);
}
servlet.service(
(ServletRequest) requestFacade,
(ServletResponse) responseFacade);
public void destroy() {
for(Servlet servlet : servlets.values()) {
servlet.destroy();
}
}
至少你要知道init()、
service()與destroy()…
10. Web 容器與 Servlet
你管的就是這些?
當然更多!不只ServletRequest、
ServletResponse、
ServletConfig、Servlet這些…
對了!這個範例改寫自這邊…
http://onjava.com/pub/a/onjava/2003/05/14/j
ava_webserver.html
13. public void run() {
while (running) {
採用Thread Pool...XD
// Allocate a new worker thread
MasterSlaveWorkerThread workerThread = createWorkerThread();
// Accept the next incoming connection from the server socket
...
Socket socket = acceptSocket();
workerThread.assign(socket);
}
..
}
沒原始碼沒真相...XD
14. 從 HTTP 請求到 service()
接下來快轉一下…來到了Http11Processor…
我應該說過我負責建立Request與
Response物件吧…這個類別剖析HTTP
並設定Request、Response
15. 從 HTTP 請求到 service()
在Http11Processor的process()中,
呼叫 adapter.service()
adapter.service(request, response);
再來的話這邊的request、response會被
org.apache.catalina.connector套
件中的Request、Response包裹起來...
WHY?
16. 從 HTTP 請求到 service()
package org.apache.catalina.connector;
public class Request implements HttpServletRequest {
…
}
package org.apache.catalina.connector;
public class Response implements HttpServletResponse {
…
}
17. 從 HTTP 請求到 service()
再快轉一下…Request與Response送到容器
ContainerBase,最後來到StandardWrapper…
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
你要注意一下loadServlet()方法…
回憶…
•Servlet第一次被請求時會被載入…
•建立ServletConfig…
•執行init()…
•呼叫service()…
18. if (classLoader != null) {
classClass = classLoader.loadClass(actualClass);
} else {
classClass = Class.forName(actualClass);
}
}
...
// Instantiate and initialize an instance of the servlet class
try {
servlet = (Servlet) classClass.newInstance();
}
...
try {
…
servlet.init(facade);
…
servlet.service(req, res);
}
facade 參考至實作 ServletConfig 的實例
protected StandardWrapperFacade facade =
new StandardWrapperFacade(this);
19. 從 HTTP 請求到 service()
SingleThreadModel 怎麼實現?你
可以自己找找看…
喵…好複雜…Orz
已經省略了很多細節了…你可以用這
個流程來研究一些更深入的…XD
27. 在service()的前後
• GenericServlet類別
– 還實作了ServletConfig介面,將容器呼叫
init()方法時所傳入的ServletConfig實例
封裝起來
– service()方法直接標示為abstract而沒有
任何的實作
• HTTP相關服務流程定義在HttpServlet
的service()方法
28. 在service()的前後
protected void service(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod(); // 取得請求的方法
if (method.equals(METHOD_GET)) { // HTTP GET
// 略...
doGet(req, resp);
// 略 ...
} else if (method.equals(METHOD_HEAD)) { // HTTP HEAD
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
// 略 ...
throws ServletException, resp);
doHead(req, IOException {
String protocol if req.getProtocol();
} else = (method.equals(METHOD_POST)) { // HTTP POST
String msg = lStrings.getString("http.method_get_not_supported");
// 略 ...
if (protocol.endsWith("1.1")) {
doPost(req, resp);
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else if (method.equals(METHOD_PUT)) { // HTTP PUT
} else { // 略 ...
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
}
29. 在service()的前後
這實現了Template Method模式。。。
現在好像談過了幾個模式?Worker
Thread、Interceptor Filter、Template
Method…
30. 在service()的前後
來談一下Session怎麼建好了…知道
Session預設用Cookie儲存Session Id
吧…
餅乾我知道…
31. 在service()的前後
if (connector.getEmptySessionPath()
&& isRequestedSessionIdFromCookie()) {
session = manager.createSession(getRequestedSessionId());
} else {
session = manager.createSession(null);
}
public Session createSession(String sessionId) {
Session session = createEmptySession()
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(this.maxInactiveInterval);
if (sessionId == null) {
sessionId = generateSessionId();
session.setId(sessionId);
sessionCounter++;
return (session);
}
32. 在service()的前後
Servlet 談好久了…要來談一
下 JSP 嗎? …XD
你應該看一下org.apache.catalina.
session.StandardSession,了解
Session怎麼儲存…好吧!接下來換個口味好
了…
33. 從 JSP 到 Servlet
跟你下棋的其實是一
隻貓…@.@
34. 從 JSP 到 Servlet
使用JSP不是比較簡單嗎? …
在我的世界中真正服務的只有Servlet…沒有
JSP…
35. 從 JSP 到 Servlet
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
<title>SimpleJSP</title>
</head>
<body>
<h1><%= new java.util.Date() %></h1>
</body>
</html>
36. 從 JSP 到 Servlet
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp
extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
// 略...
public void _jspInit() {
// 略...
}
public void _jspDestroy() {
}
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException, ServletException {
// 略...
}
}
38. 從 JSP 到 Servlet
public abstract class HttpJspBase extends HttpServlet
implements HttpJspPage {
// 略...
public final void init(ServletConfig config)
throws ServletException {
super.init(config);
jspInit();
_jspInit();
}
// 略...
public final void destroy() {
jspDestroy();
_jspDestroy();
}
public final void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
_jspService(request, response);
}
// 略...
}
39. 從 JSP 到 Servlet
• 在<%!與%>之間宣告的程式碼,都將轉譯
為Servlet中的類別成員或方法
• <%與%>之間所包括的內容,將被轉譯為
Servlet原始碼_jspService()方法中的內
容
• <%=與%>運算式元素中的運算式,會直接
轉譯為out物件print()輸出時的指定內
容
40. 從 JSP 到 Servlet
• 隱含物件,其實就是_jspService()中的
區域參考名稱…
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
這麼說Servlet作的到的,JSP
都作的到 …
原則上是…因為都是轉成Servlet…只不過角色
職責不同…這是另一個故事了…
42. 自訂標籤處理
聽說使用 JSTL 就不用吃義大利麵 …
那是新的 HTML 標籤嗎?。。XD
別傻了...我只處理 Java 的東西…每個標籤
後面都還是 Java 物件在處理事情。。XD
44. 自訂標籤處理
• 當JSP網頁中包括Simple Tag自訂標籤 ,在轉
譯之後...
1. 建立自訂標籤處理器實例。
2. 呼叫標籤處理器的setJspContext()方法設定
PageContext實例。
3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理
器的setParent()方法,並傳入外層標籤處理器的
實例。
4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的
setTest()方法來設定)。
5. 呼叫標籤處理器的setJspBody()方法設定
JspFragment實例。
6. 呼叫標籤處理器的doTag()方法。
7. 銷毀標籤處理器實例。
45. <f:choose>
<f:when test="${user.valid}">
<h1>${user.name}登入成功</h1>
</f:when>
..
</f:choose>
cc.openhome.WhenTag _jspx_th_f_005fwhen_005f0 =
new cc.openhome.WhenTag();
...
_jspx_th_f_005fwhen_005f0.setJspContext(_jspx_page_context);
_jspx_th_f_005fwhen_005f0.setParent(_jspx_parent);
…
_jspx_th_f_005fwhen_005f0.setTest(…);
_jspx_th_f_005fwhen_005f0.setJspBody(new Helper(…));
_jspx_th_f_005fwhen_005f0.doTag();
…
這代表用Simple Tag實作標籤時,你的標籤處
理器不能太肥大…
47. 自訂標籤處理
• 當JSP中遇到TagSupport自訂標籤時
1. 嘗試從標籤池(Tag Pool)找到可用的標籤物件,如果找到就
直接使用,如果沒找到就建立新的標籤物件。
2. 呼叫標籤處理器的setPageContext()方法設定
PageContext實例。
3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理器的
setParent()方法,並傳入外層標籤處理器的實例。
4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的setTest()方
法來設定)。
5. 呼叫標籤處理器的doStartTag()方法,並依不同的傳回值決
定是否執行本體或呼叫doAfterBody()、doEndTag()方法。
6. 將標籤處理器實例置入標籤池中以便再度使用。
48. 自訂標籤處理
org.apache.taglibs.standard.tag.rt.core.WhenTag
_jspx_th_c_005fwhen_005f0 =
(org.apache.taglibs.standard.tag.rt.core.WhenTag)
_005fjspx_005ftagPool_005fc_005fwhen_0026_005ftest.get(
org.apache.taglibs.standard.tag.rt.core.WhenTag.class);
...
_005fjspx_005ftagPool_005fc_005fwhen_0026_005ftest.reuse(
_jspx_th_c_005fwhen_005f0);
這代表用TagSupport實作標籤時,必須注意
是否要重置標籤處理器的狀態。。
Pool的工作是由org.apache.jasper.
runtime.TagHandlerPool完成…
49. 自訂標籤處理
public Tag get(Class handlerClass) throws JspException {
Tag handler = null;
synchronized( this ) {
if (current >= 0) {
handler = handlers[current--];
return handler;
}
}
try {
Tag instance = (Tag) handlerClass.newInstance();
…
}
public void reuse(Tag handler) {
synchronized( this ) {
if (current < (handlers.length - 1)) {
handlers[++current] = handler;
return;
}
}
…
}
50. 自訂標籤處理
我記得還有個 Tag File 的東西…
是啦!不過…Tag File 會被我轉成 Simple
Tag 的實作…XD
package org.apache.jsp.tag.web;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class Errors_tag
extends javax.servlet.jsp.tagext.SimpleTagSupport
implements org.apache.jasper.runtime.JspSourceDependent {