這部分結合之前的幾章一起看,以免遺漏。
http://blog.163.com/qqabc20082006@126/blog/static/2292852520106249123739/
Servlet
什麼是Servlet
Servlet是一個JavaEE組件,是在服務器端運行以處理客戶端請求並作出響應的程序。
Servlet何時被創建
1,默認情況下,當WEB客戶第一次請求訪問某個Servlet的時候,WEB容器將創建這個Servlet的實例。
2,當web.xml文件中如果<servlet>元素中指定了<load-on-startup>子元素時,Servlet容器在啓動web服務器時,將按照順序創建並初始化Servlet對象。
注意:在web.xml文件中,某些Servlet只有<serlvet>元素,沒有<servlet-mapping>元素,這樣我們無法通過url的方式訪問這些Servlet,這種Servlet通常會在<servlet>元素中配置一個<load-on-startup>子元素,讓容器在啓動的時候自動加載這些Servlet並調用init()方法,完成一些全局性的初始化工作。
http://www.cnblogs.com/cuiliang/archive/2011/10/21/2220671.html
Servlet的執行過程
首先,客戶端發送請求到服務器端;
其次,服務器端根據web.xml文件中的Servlet相關配置信息,將客戶端請求轉發到相應的Servlet;
之後,Servlet會根據request對象中封裝的用戶請求與數據庫進行交互,返回數據之後,Servlet會將返回的數據封裝到response對象中;
此時,控制權從Servlet重新回到服務器端,最後,服務器端將響應信息返回給客戶端,並且跳轉到相應的頁面。
Servlet生命週期分爲三個階段:
1,初始化階段 調用init()方法
2,響應客戶請求階段 調用service()方法
3,終止階段 調用destroy()方法
Servlet初始化階段
在下列時刻Servlet容器裝載Servlet:
1,Servlet容器啓動時自動裝載某些Servlet,實現它只需要在web.XML文件中的<Servlet></Servlet>之間添加如下代碼:
<loadon-startup>
1
</loadon-startup>
2,在Servlet容器啓動後,客戶首次向Servlet發送請求
3,Servlet類文件被更新後,重新裝載Servlet
Servlet被裝載後,Servlet容器創建一個Servlet實例並且調用Servlet的init()方法進行初始化。
在Servlet的整個生命週期內,init()方法只被調用一次。
Servlet響應請求階段
對於用戶到達Servlet的請求,Servlet容器會創建特定於這個請求的ServletRequest對象和ServletResponse對象,然後調用Servlet的service方法。service方法從ServletRequest對象獲得客戶請求信息,處理該請求,並通過ServletResponse對象向客戶返回響應信息。
對於Tomcat來說,它會將傳遞過來的參數放在一個Hashtable中,該Hashtable的定義是:
private
Hashtable<String String[]> paramHashStringArray = new Hashtable<String String[]>(); |
這是一個String-->String[]的鍵值映射。
HashMap線程不安全的,Hashtable線程安全。
-----------------------------------------------------------------------------------------------------------------------------------
Servlet終止階段
當WEB應用被終止,或Servlet容器終止運行,或Servlet容器重新裝載Servlet新實例時,Servlet容器會先調用Servlet的destroy()方法,在destroy()方法中可以釋放掉Servlet所佔用的資源。
Servlet類的繼承關係
客戶發送一個請求,Servlet是調用service()方法對請求進行響應的。
通過源代碼可見,service()方法中對請求的方式進行了匹配,選擇調用doGet,doPost等這些方法,然後再進入對應的方法中調用邏輯層的方法,實現對客戶的響應。
在Servlet接口和GenericServlet中是沒有doGet,doPost等等這些方法的,HttpServlet中定義了這些方法,但是都是返回error信息,所以,我們每次定義一個Servlet的時候,都必須實現doGet或doPost等這些方法。
每一個自定義的Servlet都必須實現Servlet的接口,Servlet接口中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。
GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet接口。
而HttpServlet繼承於GenericServlet,因此HttpServlet也實現了Servlet接口。所以我們定義Servlet的時候只需要繼承HttpServlet即可。
Servlet接口和GenericServlet是不特定於任何協議的,而HttpServlet是特定於HTTP協議的類,所以HttpServlet中實現了service()方法,並將請求ServletRequest,ServletResponse強轉爲HttpRequest和HttpResponse。
public
void service(ServletRequest req,ServletResponse res) throws ServletException,IOException { HttpRequest request; HttpResponse response; try { req = (HttpRequest)request; res = (HttpResponse)response; } catch (ClassCastException e) { throw new ServletException( "non-HTTP request response" ); } service(request,response); } |
代碼的最後調用了HTTPServlet自己的service(request,response)方法,然後根據請求去調用對應的doXXX方法,因爲HttpServlet中的doXXX方法都是返回錯誤信息,
protected void doGet(HttpServletRequest res,HttpServletResponse resp) throws ServletException,IOException { String protocol = req.getProtocol(); String msg = IStrings.getString( "http.method_get_not_supported" ); if (protocol.equals( "1.1" )) { resp.sendError(HttpServletResponse.SC.METHOD.NOT.ALLOWED,msg); } esle { resp.sendError(HttpServletResponse.SC_BAD_REQUEST,msg); } } |
所以需要我們在自定義的Servlet中override這些方法!
Servlet的實現:Servlet接口->GenericServlet類->HttpServlet類->用戶自定義的Servlet。
Servlet與JSP的關係及區別
Servlet是JSP的基礎,JSP是Servlet技術的擴展。JSP運行之前首先將編譯爲一個Servlet。
JSP側重於視圖;Servlet主要用於控制業務邏輯。
Servlet API中的forward()與redirect()的區別?
請求轉發:forward是Request對象的方法,它是在服務器端執行,並且始終在同一個Request域中,所以頁面間可以共享Request對象中的資源。轉發後,客戶端瀏覽器地址不會改變;請求轉發性能優於重定向;
<jsp:forward>
servlet中的實現:
RequestDispatcher rd = request.getRequestDispatcher("login2");
rd.forward(request, response);-----------------服務器端將request和response對象直接轉發給目的servlet處理。reques不變。
重定向:redirect是response對象的方法,發生在客戶端瀏覽器,它有兩次Request請求,第二次請求將丟失第一次Request中的資源。重定向之後,客戶端瀏覽器地址發生改變。
servlet中的實現:response.sendRedirect("error.jsp");----------------響應發給客戶端,客戶端再向"error.jsp"發出請求。request改變。
Servlet API中的include()
<jsp:include>
servlet中的實現:
RequestDispatcher rd = request.getRequestDispatcher("login2");
rd.include(request, response);-----------------服務器端將request和response對象直接交給被包含的servlet處理。reques不變。
Servlet是單實例的,但有兩種訪問模式:多線程以及單線程訪問,前者會有線程安全方面的問題,後者有請求排隊等待的問題。
Servlet如何同時處理多個請求?
Servlet採用多線程來處理多個請求的同時訪問。Servlet容器通過線程池來管理維護服務請求。所謂線程池,相當於數據庫連接池,實際上是等待執行代碼的一組線程,叫做工作者線程。Servlet容器通過一個調度線程來管理工作者線程。
· 當容器收到一個Servlet的訪問請求,調度者線程就從線程池中選出一個工作者線程,將用戶請求傳遞給該線程,然後由該線程處理Servlet的service()方法;
· 當這個線程在執行的時候,容器收到一個新的請求,調度者線程再次從線程池中選出一個新的工作者線程;
· 當容器同時收到對同一個Servlet的多個請求時,那麼Servlet的service方法將在多線程中併發執行。
注:1.Servlet容器默認採用單實例多線程的方式來處理請求。這樣減少了產生Sevlet實例的開銷,提升了對請求的響應時間;
2.對於Tomcat容器來講,可以在其server.xml中通過<Connector>中設置線程池中的線程數目。
Servlet也可以採用單線程模式!
如何開發線程安全的Servlet?
Servlet容器採用多線程來處理請求,提高性能的同時也造成了線程安全問題。要開發線程安全的Servlet應該從一下幾個方面進行:
1. 變量的線程安全; 多線程並不共享局部變量,所以我們要儘可能的在Servlet中使用局部變量;
2. 代碼塊的線程安全; 使用同步塊Synchronized,防止可能調用的代碼塊;但是要注意的是,要儘可能得縮小同步代碼的方範圍,不要在service方法和響應方法上直接使用同步,這會嚴重影響性能。
3. 屬性的線程安全;ServletContext,HttpSession,ServletRequest對象中屬性;
4. 使用同步集合; 使用Vector代替ArrayList,使用HashTable代替HashMap;
5. 不要在Servlet中創建自己的線程來完成某個功能; Servlet本身就是多線程的,如果再創建新的線程,將會導致線程執行複雜化,出現線程安全問題;
6. 在多個Servlet中,對外部對象,比如:文件;進行修改操作一定要加鎖,做到互斥訪問;
如何實現servlet的單線程模式?
javax.servlet.SingleThreadModel接口是一個標識接口,如果一個Servlet實現了這個接口,那Servlet容器將保證在一個時刻僅有一個線程可以在給定的servlet實例的service方法中執行,將其他所有請求進行排隊。實現方法:<%@ page isThreadSafe="false" %>