Servlet(2)
一、偽代碼演示Tomcat的內部代碼運行
1)、通過映射找到servlet-class的內容,字符串:com.gqx.servlet.FirstServlet
2)、通過反射構造構造FirstServlet對象
2、1 得到字節碼(class)文件對象
Class clazz=class.forName("com.gqx.servlet.FirstServlet");
2、2調用無參的構造方法來構造對象
Object obj =clazz.newInstance(); -->serlvet的構造方法被激活
3)、創建ServletConfig對象,通過反射調用init方法
3.1 得到方法對象
Method m = clazz.getDeclareMethod("init",ServletConfig.class);
3.2 調用方法
m.invoke(obj,config); --2.servlet的init方法被調用
4)創建request,response對象,通過反射調用service方法
4.1 得到方法對象
Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
4.2 調用方法
m.invoke(obj,request,response); --3.servlet的service方法被調用
5)當tomcat服務器停止或web應用重新部署,通過反射調用destroy方法
5.1 得到方法對象
Method m = clazz.getDeclareMethod("destroy",null);
5.2 調用方法
m.invoke(obj,null); --4.servlet的destroy方法被調用
用時序圖來演示servlet的生命周期
Servlet的自動加載
默認情況下,第一次訪問servlet的時候創建servlet對象。如果servlet的構造方法或init方法中執行了比較多的邏輯代碼,那麽導致用戶第一次訪問sevrlet的時候比較慢。 改變servlet創建對象的時機: 提前到加載web應用的時候!!!
在servlet的配置信息中,加上一個<load-on-startup>即可!!
如下
<servlet> <servlet-name>LifeDemo</servlet-name> <servlet-class>gz.itcast.c_life.LifeDemo</servlet-class> <!-- 讓servlet對象自動加載 --> <load-on-startup>1</load-on-startup> <!-- 註意: 整數值越大,創建優先級越低!!--> </servlet>
有參的init方法和無參的init方法
有參的init方法中實現了無參的init方法,其源代碼中調用了this.init();所以我們一般都會在無參的init方法中寫入代碼
Servlet的多線程並發問題
註意: servlet對象在tomcat服務器是單實例多線程的。
因為servlet是多線程的,所以當多個servlet的線程同時訪問了servlet的共享數據,如成員變量,可能會引發線程安全問題。
解決辦法:
1)把使用到共享數據的代碼塊進行同步(使用synchronized關鍵字進行同步)
2)建議在servlet類中盡量不要使用成員變量。如果確實要使用成員,必須同步。而且盡量縮小同步代碼塊的範圍。(哪裏使用到了成員變量,就同步哪裏!!),以避免因為同步而導致並發效率降低。
實例代碼如下:
package com.gqxing.servlet2; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ThreadDemo extends HttpServlet { /** * 多線程安全問題 * 案例:訪問網站的第幾個人數 */ int count=1; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); response.getWriter().write("你現在是當前網站的第"+count+"個訪客!"); //為了效果,這裏用sleep方法去讓線程同步 try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //當多個線程同時訪問這裏獲取相同的count值的時候會發生線程安全問題。 count++; } }
效果如圖
這個時候要加入避免出現線程安全的機制
package com.gqxing.servlet2; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ThreadDemo2 extends HttpServlet { /** * 多線程安全問題 * 案例:訪問網站的第幾個人數 */ int count=1; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //為當前訪問的線程加鎖, synchronized (ThreadDemo2.class) {//鎖線程必須是唯一的,可以用當前的字節碼對象 response.getWriter().write("你現在是當前網站的第"+count+"個訪客!"); } count++; } }
Servlet(2)