Servlet容器的啟動(Tomcat為例)


一.容器簡介

在tomcat容器等級中,context容器直接管理servlet在容器中的包裝類Wrapper,所以Context容器如何運行將直接影響servlet的工作方式。

tomcat容器模型如下:

一個context對應一個web工程,在tomcat的配置文件server.xml中,可以發現context的配置(在eclipse工程中,可在部署路徑的conf文件夾zhoing找到)

1 Context docBase="/Users/wongrobin/all/projects/tech-test/java-test/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/base-webapp" path="/base-webapp" reloadable="true" source="org.eclipse.jst.j2ee.server:base-webapp"/>  

二.啟動servlet

Tomcat7增加了一個啟動類:

1 org.apache.catalina.startup.Tomcat 

創建一個Tomcat的一個實例對象並調用start方法就可以啟動tomcat。

還可以通過這個對象來增加和修改tomcat的配置參數,如可以動態增加context,servlet等。

 在tomcat7中提供的example中,看是如何添加到context容器中:

1 Tomcat tomcat = getTomcatInstance();  
2 File appDir = new File(getBuildDirectory(), "webapps/examples");  
3 tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());  
4 tomcat.start();  
5 ByteChunk res = getUrl("http://localhost:" + getPort() +  
6                        "/examples/servlets/servlet/HelloWorldExample");  
7 assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0); 

這段代碼創建了一個Tomcat實例並新增了一個WEB應用,然后啟動Tomcat並調用其中的一個HelloWorldExampleServlet。

Tomcat的addWebap方法的代碼如下:

 1 public Context addWebapp(Host host, String url, String path) {  
 2     silence(url);  
 3     Context ctx = new StandardContext();  
 4     ctx.setPath( url );  
 5     ctx.setDocBase(path);  
 6     if (defaultRealm == null) {  
 7         initSimpleAuth();  
 8     }  
 9     ctx.setRealm(defaultRealm);  
10     ctx.addLifecycleListener(new DefaultWebXmlListener());  
11     ContextConfig ctxCfg = new ContextConfig();  
12     ctx.addLifecycleListener(ctxCfg);  
13     ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");  
14     if (host == null) {  
15         getHost().addChild(ctx);  
16     } else {  
17         host.addChild(ctx);  
18     }  
19     return ctx;  
20 }  

一個WEB應用對應一個context容器,也就是servlet運行時的servlet容器。添加一個web應用時將會創建一個StandardContext容器,並且給這個context容器設置必要的參數,url和path分別代表這個應用在tomcat中的訪問路徑和這個應用實際的物理路徑,這兩個參數與tomcat配置中的兩個參數是一致的。其中一個最重要的一個配置是ContextConfig,這個類會負責整個web應用配置的解析工作。
最后將這個context容器加入到父容器host中。

接下來會調用tomcat的start方法啟動tomcat。

Tomcat的啟動邏輯是基於觀察者模式的,所有的容器都會繼承Lifecycle接口,它管理着容器的整個生命周期,所有容器的修改和狀態改變都會由它通知已經注冊的觀察者。

Tomcat啟動的時序如下:

當context容器初始狀態設置Init時,添加到context容器的listener將會被調用。ContextConfig繼承了LifecycleListener接口,它是在調用Tomcat.addWebapp時被加入到StandardContext容器中的。ContextConfig類會負責整個WEB應用的配置文件的解析工作。

ContextConfig的init方法將會主要完成一下工作:

 

  • 創建用於解析XML配置文件的contextDigester對象
  • 讀取默認的context.xml文件,如果存在則解析它
  • 讀取默認的Host配置文件,如果存在則解析它
  • 讀取默認的Context自身的配置文件,如果存在則解析它
  • 設置Context的DocBase

ContextConfig的init方法完成后,Context容器會執行startInternal方法,這個方法包括如下幾個部分:

 

  • 創建讀取資源文件的對象
  • 創建ClassLoader對象
  • 設置應用的工作目錄
  • 啟動相關的輔助類,如logger,realm,resources等
  • 修改啟動狀態,通知感興趣的觀察者
  • 子容器的初始化
  • 獲取ServletContext並設置必要的參數
  • 初始化“load on startuo”的Servlet

三.Web應用的初始化

WEB應用的初始化工作是在ContextConfig的configureStart方法中實現的,應用的初始化工作主要是解析web.xml文件,這個文件是一個WEB應用的入口。

Tomcat首先會找globalWebXml,這個文件的搜索路徑是engine的工作目錄下的org/apache/catalina/startup/NO-DEFAULT_XML或conf/web.xml。接着會找hostWebXml,這個文件可能會在System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default中。接着尋找應用的配置文件examples/WEB-INF/web.xml,web.xml文件中的各個配置項將會被解析成相應的屬性保存在WebXml對象中。接下來會講WebXml對象中的屬性設置到context容器中,這里包括創建servlet對象,filter,listerner等,這些在WebXml的configureContext方法中。

下面是解析servlet的代碼對象:

 1 for (ServletDef servlet : servlets.values()) {  
 2     Wrapper wrapper = context.createWrapper();  
 3     String jspFile = servlet.getJspFile();  
 4     if (jspFile != null) {  
 5         wrapper.setJspFile(jspFile);  
 6     }  
 7     if (servlet.getLoadOnStartup() != null) {  
 8         wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());  
 9     }  
10     if (servlet.getEnabled() != null) {  
11         wrapper.setEnabled(servlet.getEnabled().booleanValue());  
12     }  
13     wrapper.setName(servlet.getServletName());  
14     Map<String,String> params = servlet.getParameterMap();  
15     for (Entry<String, String> entry : params.entrySet()) {  
16         wrapper.addInitParameter(entry.getKey(), entry.getValue());  
17     }  
18     wrapper.setRunAs(servlet.getRunAs());  
19     Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();  
20     for (SecurityRoleRef roleRef : roleRefs) {  
21         wrapper.addSecurityReference(roleRef.getName(), roleRef.getLink());  
22     }  
23     wrapper.setServletClass(servlet.getServletClass());  
24     MultipartDef multipartdef = servlet.getMultipartDef();  
25     if (multipartdef != null) {  
26         if (multipartdef.getMaxFileSize() != null &&  
27             multipartdef.getMaxRequestSize()!= null &&  
28             multipartdef.getFileSizeThreshold() != null) {  
29                 wrapper.setMultipartConfigElement(new MultipartConfigElement(  
30                                                       multipartdef.getLocation(),  
31                                                       Long.parseLong(multipartdef.getMaxFileSize()),  
32                                                   Long.parseLong(multipartdef.getMaxRequestSize()),  
33                                                   Integer.parseInt(  
34                                                   multipartdef.getFileSizeThreshold())));  
35         } else {  
36         wrapper.setMultipartConfigElement(new MultipartConfigElement(  
37                                               multipartdef.getLocation()));  
38         }  
39     }  
40     if (servlet.getAsyncSupported() != null) {  
41         wrapper.setAsyncSupported(  
42             servlet.getAsyncSupported().booleanValue());  
43     }  
44     context.addChild(wrapper);  
45 }  

上面的代碼將servlet容器包裝成context容器中的StandardWrapper。StandardWrapper是tomcat容器中的一部分,它具有容器的特征,而Servlet作為一個獨立的web開發標准,不應該強制耦合在tomcat中。

除了將Servlet包裝成StandardWrapper並作為子容器添加到Context中外,其他所有的web.xml屬性都被解析到Context中。

四.創建Servlet實例

前面完成了servlet的解析工作,並且被包裝成了StandardWrapper添加到Context容器中,但是它仍然不能為我們工作,它還沒有被實例化。

1.創建

如果Servlet的load-on-startup配置項大於0,那么在Context容器啟動時就會被實例化。

前面提到的在解析配置文件時會讀取默認的globalWebXml,在conf下的web.xml文件中定義了一些默認的配置項,其中定義了兩個Servlet,分別是org.apache.catalina.servlets.DefaultServlet和org.apache.jsper.servlet.JspServelt,它們的load-on-startup分別是1和3,也就是當tomcat啟動時這兩個servlet就會被啟動。

創建Servlet實例的方式是從Wrapper.loadServlet開始的,loadServlet方法要完成的就是獲取servletClass,然后把它交給InstanceManager去創建一個基於servletClass.class的對象。如果這個Servlet配置了jsp-file,那么這個servletClass就是在conf/web.xml中定義的org.apache.jasper.servlet.JspServlet。

2.初始化

初始化Servlet在StandardWrapper的initServlet方法中,這個方法很簡單,就是調用Servlet的init()方法,同時把包裝了StandardWrapper對象的StandardWrapperFacade作為ServletConfig傳給Servlet。

如果該Servlet關聯的是一個JSP文件,那么前面初始化的就是JspServlet,接下來會模擬一次簡單請求,請求調用這個JSP文件,以便編譯這個JSP文件為類,並初始化這個類。

這樣Servlet對象的初始化就完成了。

3.容器默認Servlet

每個servlet容器都有一個默認的servlet,一般都叫做default。

例如:tomcat中的 DefaultServlet 和 JspServlet (上面的部分)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM