1. 程式人生 > >Servlet(2)

Servlet(2)

解決辦法 trace 使用 .cn port 加載 set 配置信息 rac

一、偽代碼演示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)