Servlet與JSP綜合講述
Servlet和JSP的概念
Servlet是Sun推出的用于實現CGI(通用網關接口)的java語言版本,它不但具有跨平臺的特性,而且還以多線程的方式為用戶提供服務而不必為每個請求都啟動一個線程,因此其效率要比傳統的CGI程序要高很多.
JSP和MS的ASP類似,它把JSP標簽嵌入到HTML格式的網頁中,這樣對程序員和網頁編輯人員都很方便,JSP天生就是為表現層設計的.
實際上,Servlet只是繼承了HttpRequest的Java類,而JSP最終也會被Servlet引擎翻譯成Servlet并編譯執行,JSP的存在主要是為了方便表現層.
Servlet與JSP之間的區別,決定了Servlet與JSP在MVC構架模式中的不同角色.Servlet一般作為MVC中的控制器,JSP一般作為MVC中的視圖.
Servlet的生命周期
Servlet有三個生命周期:初始化,執行和結束,它們分別對應Servlet接口中的init,service和destroy三個函數.
初始化時期:當servlet被servlet容器(如tomcat)載入后,servlet的init函數就會被調用,在這個函數可以做一些初始化工作.init函數只會在servlet容器載入servlet執行一次,以后無論有多少客戶端訪問這個Servlet,init函數都不會被執行.
執行期:servlet采用多線程方式向客戶提供服務,當有客戶請求來到時, service會被用來處理它.每個客戶都有自己的service方法,這些方法接受客戶端請求,并且發揮相應的響應.程序員在實現具體的Servlet時,一般不重載service方法,服務器容器會調用service方法以決定doGet,doPost,doPut,doDelete中的一種或幾種,因此應該重載這些方法來處理客戶端請求.]
結束期:該時期服務器會卸載servlet,它將調用destroy函數釋放占用的資源,注意Web服務器是有可能崩潰的,destroy方法不一定會被執行.
如何開發和部署一個Servlet
1)從java.servlet.http.HttpServlet繼承自己的servlet類.
2)重載doGet或doPost方法來處理客戶請求(一般是doPost,其安全性較好),如果要在加載Servlet時被加載時進行初始化操作,可以重載init方法.
3)在web.xml中配置這個servlet.其中servlet-class用來制定這個servlet的類全名,servlet-name用來標識這個servlet,它可以是任意的字符串,不一定要和類文件名一致.url-pattern用來表示servlet被映射到的url模式.
<!– Servlet在Web.xml中的配置示例 -->
<servlet>
<servlet-name>ShowPageServlet</servlet-name>
<servlet-class>
com.sitinspring.action.ShowPageServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowPageServlet</servlet-name>
<url-pattern>/ShowPage</url-pattern>
</servlet-mapping>
開發一個啟動時就被執行的Servlet
一般的Servlet都是在有來自客戶端請求時才會執行,要讓它在啟動時就執行需要在配置中進行一些特殊設置,如右.
右邊的代碼中, load-on-startup說明了服務器一啟動就加載并初始化它,0代表了加載它的優先級,注意它必須是一個正數,而且值小的要比值大的先加載.debug制定輸出調試信息的級別,0為最低.
這樣的servlet在用于讀取WebApp的一些初始化參數很有用處,如取得配置文件的地址,設置log4j和得到WebApp的物理路徑等.右邊配置的servlet就是用來初始化log4j的.
<!-- InitServlet -->
<servlet>
<servlet-name>log4j-init</servlet-name>
<servlet-class>
com.sitinspring.action.Log4jInit
</servlet-class>
<init-param>
<param-name>log4j</param-name>
<param-value>WEB-INF/classes/log4j.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Serlet中出現的線程不安全的問題
Servlet是運行在多線程的服務器上的,它對每個用戶的請求創建的是線程而不是進程,因此在高效的同時也帶來了數據同步和一致性的問題.
服務器值實例化一個Servlet/JSP實例,然后在多個處理線程中調用該實例的相關方法來處理請求,因此servlet的成員變量可能會被多個線程調用該實例的相關方法改變,將有可能帶來問題.這也是大家在眾多的Servlet中很少看到成員變量的原因.

public class ThreadUnsafeServlet extends HttpServlet {
private String unsafeString="";
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, java.io.IOException {
request.setCharacterEncoding("UTF-8");
unsafeString=request.getParameter("str");
try{
Thread.sleep(5000);
}
catch(Exception ex){
ex.printStackTrace();
}
PrintWriter out=response.getWriter();
out.println(unsafeString);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, java.io.IOException {
doPost(request, response);
}
}
關于重定向的兩種方法
在servlet中,重定向一般是通過HttpResponse的sendRedirect()方法或RequestDispatcher的forward方法來實現的.
sendRedirect的參數可以是相對或絕對地址,如果以/開頭容器將認為相對于當前Web引用的根.這種請求將導致客戶端瀏覽器請求URL跳轉,而且從Browser的地址欄中可以看到新的Url地址.另外使用這個方法時,前一個頁面的狀態不會被帶到下一個頁面,通過request.getAttribute(“XXXX”)方法將得到空值.
RequestDispatcher是一個Web資源的包裝器,可以用來把當前請求傳遞到該資源,而且客戶端瀏覽器的地址欄也不會顯示為轉向后的地址.另外此方法還可以將請求發送到任意一個服務器資源.
如果兩種方法都能達到要求,最好使用forward方法,它比sendRedirect安全而高效.
獲取當前絕對路徑
Servlet/JSP有時需要對擁有的資源進行操作,這就要求得到它們所在的絕對路徑.此時可以使用ServletContext接口提供的方法來得到當前應用所在的絕對路徑,代碼如下:
ServletContext sct = getServletContext();
String realPath=sct.getRealPath("/");
注: ServletContext 用于Servlet和Servlet容器交換信息.