容器收到請(qǐng)求后即會(huì)調(diào)用該方法。面對(duì)大量的用戶訪問(wèn),Servlet容器一般采用“單實(shí)例,多線程”的方式管理Servlet,即Servlet容器只會(huì)創(chuàng)建一個(gè)Servlet實(shí)例,針對(duì)不同的用戶訪問(wèn)將開(kāi)啟不同的線程運(yùn)行service方法,Servlet容器一般會(huì)維護(hù)一個(gè)線程池去管理這些線程。鑒于Servlet容器的這種管理方式,對(duì)于service方法而言應(yīng)注意線程安全的問(wèn)題。
一般在實(shí)際中常常使用的是基于HTTP協(xié)議的Servlet,作為基于HTTP協(xié)議的Servlet可以通過(guò)繼承javax.servlet.HttpServlet得到。HttpServlet類(lèi)實(shí)現(xiàn)了Servlet接口,并在service方法中根據(jù)不同的請(qǐng)求(get或post等)調(diào)用相應(yīng)的方法(doGet或doPost)。一般情況下,作為HttpServlet的子類(lèi)可以重寫(xiě)其doGet方法用于處理Get請(qǐng)求,重寫(xiě)doPost方法用于處理Post請(qǐng)求。
另外HttpServlet可以重寫(xiě)init()和destory()方法
- init()方法用于定義初始化邏輯,Servlet一創(chuàng)建后即會(huì)被調(diào)用。
- destory()方法將在Servlet實(shí)例銷(xiāo)毀前調(diào)用。
一個(gè)HttpServlet需要有特定的部署描述符(web.xml,置于應(yīng)用的/WEB-INF中)指定其特征:其定義如下:
<servlet>
<servlet-name>Tst</servlet-name>
<servlet-class>test.TestServlet</servlet-class>
<init-param>
<param-name>someKey</param-name>
<param-value>someValue</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Tst</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
- 子元素指定Serlvet的名稱(chēng),作為該Servlet的唯一標(biāo)識(shí)。
- 子元素用于指定Servlet的類(lèi)名。
- 的子元素用于定義所謂基于鍵-值對(duì)的初始化參數(shù),Servlet對(duì)象可以通過(guò)調(diào)用ServletConfig對(duì)象的getInitParameter(String name)獲取制定指定鍵的對(duì)應(yīng)值。 可以有多個(gè)元素,用于定義Servlet在工作時(shí)要獲取的某些初始化信息(如:要讀取的某些屬性文件的路徑等 )。
- 的子元素用于指定是在Servlet容器啟動(dòng)時(shí)即創(chuàng)建該Servlet對(duì)象,還是在Serlvet容器接收到針對(duì)該Servlet的請(qǐng)求后再實(shí)例化該Servlet對(duì)象。的值為負(fù)數(shù)時(shí)采取后一種策略,的值為0或正數(shù)時(shí)采取前一種策略。web.xml中可能會(huì)定義多個(gè)為正數(shù)的Servlet,Servlet容器會(huì)根據(jù)其值得大小決定其實(shí)例化順序,值小的先實(shí)例化,值大的后實(shí)例化。
元素用于把特定的Servlet映射到一個(gè)URI地址,當(dāng)Servlet容器收到針對(duì)該地址發(fā)出的請(qǐng)求時(shí)即會(huì)實(shí)例化相應(yīng)的Servlet對(duì)象,并調(diào)用其特定方法。例如某個(gè)Servlet的url-pattern為"/test"則當(dāng)在瀏覽器的地址欄輸入".../應(yīng)用名/test"時(shí),Servlet容器將會(huì)訪問(wèn)
該Servlet。
作為T(mén)omcat服務(wù)器,還可以通過(guò)其他的方式訪問(wèn)Servlet,在其安裝目錄的conf子目錄下有也有一個(gè)web.xml該web.xml中定義了一些Servlet,這些Servlet屬于Tomcat內(nèi)置的Servlet,在Tomcat服務(wù)器一啟動(dòng)時(shí)即會(huì)實(shí)例化這些Servlet對(duì)象。
其中InvokerServlet的配置信息如下:
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
該Servlet用于訪問(wèn)其他的Servlet,其默認(rèn)的url-pattern配置為/servlet/*。當(dāng)我們?cè)跒g覽器的地址欄輸入“.../servlet/其他Servlet的類(lèi)名” 時(shí),則Tomcat容器將通過(guò)InvokerServlet訪問(wèn)該Servlet(即使該Servlet沒(méi)有在web.xml進(jìn)行定義)
例如:在瀏覽器的地址欄輸入".../應(yīng)用名/servlet/servet.TestServlet時(shí),Servlet容器即會(huì)對(duì)test.TestServlet進(jìn)行訪問(wèn)。這種訪問(wèn)方式將便于對(duì)Servlet的測(cè)試。
我們可以關(guān)注Tomcat另外的一個(gè)內(nèi)置Servlet:DefaultServlet可以用于列出應(yīng)用下某個(gè)目錄的內(nèi)容,該Servlet的配置信息如下:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
更改其配置信息的名為listings的初始化參數(shù)值為true時(shí),當(dāng)輸入某個(gè)應(yīng)用的目錄時(shí)DefaultServlet可以用下圖的方式列出該目錄中的內(nèi)容并可實(shí)現(xiàn)鏈接導(dǎo)航。這樣的功能會(huì)對(duì)測(cè)試有一定的幫助。
二、什么是JSP?
準(zhǔn)確的說(shuō),JSP就是Servlet,JSP是一個(gè)標(biāo)準(zhǔn)的文本文件,在第一次訪問(wèn)時(shí),Servlet會(huì)將該文件“翻譯”成Servlet,然后再實(shí)施調(diào)用。不同的應(yīng)用服務(wù)器會(huì)有不同的翻譯方式。
針對(duì)于Tomcat服務(wù)器而言,我們可以在conf/web.xml中關(guān)注定義的另外一個(gè)內(nèi)置Servlet:JspServlet,該Servlet的功能在于“翻譯”JSP并對(duì)其實(shí)施訪問(wèn),該Servlet的定義如下:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
注意該Servlet的url-pattern為“*.jsp”及所有后綴為“.jsp”等請(qǐng)求將會(huì)訪問(wèn)該Servlet,例如:在瀏覽器地址欄輸入“.../應(yīng)用名/test.jsp”將訪問(wèn)該Servlet
而該Servlet將首先獲取名為test.jsp文件的內(nèi)容將其編譯成并實(shí)施調(diào)用。
可以做一個(gè)小的實(shí)驗(yàn),在conf/web.xml中替換JspServlet對(duì)應(yīng)的url-pattern(例如:改成hey),重啟Tomcat服務(wù)器,訪問(wèn)內(nèi)容如下所示的hello.jsp(地址為".../應(yīng)用名/hello.jsp")
<html>
<head>
<title>hello.jsp</title>
</head>
<body>
<%
for (int i = 0; i <= 100; i++) {
out.println("helloworld");
}
%>
</body>
</html>
通過(guò)在瀏覽器中"單擊右鍵->查看源文件"的方式可以看到瀏覽器你收到的內(nèi)容為:
<html>
<head>
<title>hello.jsp</title>
</head>
<body>
<%
for (int i = 0; i <= 100; i++) {
out.println("helloworld");
}
%>
</body>
</html>
可見(jiàn)該jsp并沒(méi)有被解析,而是以靜態(tài)文本的方式原樣輸出。
在Tomcat安裝目錄下的\work\Catalina下可以找到JSP經(jīng)過(guò)“翻譯”后的Java源文件和編譯后類(lèi)文件,對(duì)于上面hello.jsp而言,“翻譯”后的Servlet源文件為:\work\Catalina\localhost\tst\org\apache\jsp\hello_jsp.java
該文件的主要內(nèi)容如下:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
... ... ...
public void _jspService
(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
... ... ...
out.write("<html>\r\n");
out.write("\t<head>\r\n");
out.write("\t\t<title>hello.jsp</title>\r\n");
out.write("\t</head>\r\n");
out.write("\r\n");
out.write("\t<body>\r\n");
out.write("\t\t");
for (int i = 0; i <= 100; i++) {
out.println("helloworld");
}
out.write("\r\n");
out.write("\t</body>\r\n");
out.write("</html>");
... ... ...
}
}
可以看出hell.jsp經(jīng)過(guò)“翻譯”后的類(lèi)名為hello_jsp 該類(lèi)繼承了org.apache.jasper.runtime.HttpJspBase類(lèi),而HttpJspBase類(lèi)又是HttpServlet的子類(lèi)。HttpJspBase在其service方法中調(diào)用了_jspService方法,針對(duì)特定的jsp頁(yè)面,Tomcat的JSP引擎將其“翻譯”成HttpJspBase的子類(lèi)并重寫(xiě)其_jspService方法。該類(lèi)的很多初始化內(nèi)容有JSP引擎完成。
在上面的_jspService方法中可以看到hello.jsp的“影子”,在JSP中使用<%...%>所書(shū)寫(xiě)的Java代碼被原樣的置于_jspService方法中;另外在JSP中的HTML腳本通過(guò)流對(duì)象out原樣輸出...當(dāng)然,JSP的“翻譯”不可能如此簡(jiǎn)單,因?yàn)镴SP頁(yè)面還可能寫(xiě)有指令、標(biāo)簽等復(fù)雜的結(jié)構(gòu)。
從上面的_jspService方法還可以看出,所謂JSP內(nèi)建對(duì)象(request、reponse、application、session等)其實(shí)并不神秘,它們或是_jspService方法的參數(shù)變量,或是由JSP引擎在_jspService方法中預(yù)先定義好的變量,我們?cè)贘SP的<%...%>中可以直接使用。