servlet詳解

funnyZhang發表於2017-05-15

Servlet對映路徑

Servlet配置

<!-- 配置一個servlet -->
  <!-- servlet的配置 -->
  <servlet>
    <!-- servlet的內部名稱,自定義。儘量有意義 -->
    <servlet-name>FirstServlet</servlet-name>
    <!-- servlet的類全名: 包名+簡單類名 -->
    <servlet-class>gz.itcast.a_servlet.FirstServlet</servlet-class>
  </servlet>


  <!-- servlet的對映配置 -->
  <servlet-mapping>
    <!-- servlet的內部名稱,一定要和上面的內部名稱保持一致!! -->
    <servlet-name>FirstServlet</servlet-name>
    <!-- servlet的對映路徑(訪問servlet的名稱) -->
    <url-pattern>/first</url-pattern>
  </servlet-mapping>

Servlet精確匹配和模糊匹配

Servlet精確匹配和模糊匹配

精確匹配和模糊匹配該注意的問題

1)url-pattern要麼以 / 開頭,要麼以*開頭。 例如, itcast是非法路徑。
2)不能同時使用兩種模糊匹配,例如 /itcast/*.do是非法路徑
3)當有輸入的URL有多個servlet同時被匹配的情況下:
匹配原則:
3.1 精確匹配優先。(長的最像優先被匹配)
3.2 以字尾名結尾的模糊url-pattern優先順序最低!!!

Servlet預設路徑

servlet的預設路徑(<url-pattern>/</url-pattern>)是在tomcat伺服器內建的一個路徑(在conf\web.xml中)。該路徑對應的是一個DefaultServlet(預設Servlet)。這個預設的Servlet的作用是用於解析web應用的靜態資原始檔。

問題: URL輸入http://localhost:8080/day10/index.html 如何讀取文件?
1)到當前day10應用下的web.xml檔案查詢是否有匹配的url-pattern。
2)如果沒有匹配的url-pattern,則交給tomcat的內建的DefaultServlet處理
3)DefaultServlet程式到day10應用的根目錄下查詢是存在一個名稱為index.html的靜態檔案。
4)如果找到該檔案,則讀取該檔案內容,返回給瀏覽器。
5)如果找不到該檔案,則返回404錯誤頁面。
結論:
Tomcat先找動態資源,再找靜態資源。

Servlet生命週期

Servlet程式的生命週期由tomcat伺服器控制的!

四個重要方法

來看一下HttpServlet的繼承結構
這裡寫圖片描述
這裡寫圖片描述
其中,HttpServelt繼承了GenericServlet類,而GenericServlet類實現了Servlet介面。
再看看Servlet介面:
這裡寫圖片描述
這說明HttpServlet裡面一定有這五個方法。
其中,Tomcat一定會執行的有以下四個方法:
(1)構造方法: 建立servlet物件的時候呼叫。預設情況下,第一次訪問servlet的時候建立servlet物件 只呼叫1次。證明servlet物件在tomcat是單例項的。
(2)init方法: 建立完servlet物件的時候呼叫。只呼叫1次。
(3)service方法: 每次發出請求時呼叫。呼叫n次。
(4)destroy方法: 銷燬servlet物件的時候呼叫。停止伺服器或者重新部署web應用時銷燬servlet物件只呼叫1次。
例子:

package gz.itcast.c_life;
import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LifeDemo extends HttpServlet {

    /**
     * 1.構造方法
     */
    public LifeDemo(){
        System.out.println("1.servlet物件被建立了。");
    }

    /**
     * 2.init方法
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("2.init方法被呼叫");
    }

    /**
     * 3.service方法
     */
    @Override           
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        System.out.println("3.service方法被呼叫");
    }
    /**
     * 4.destroy方法
     */
    @Override
    public void destroy() {
        System.out.println("4.servlet物件銷燬了");
    }
}

執行效果:
多次訪問地址:
這裡寫圖片描述

關閉Tomcat:
這裡寫圖片描述

時序圖演示

以FirstServlet為例:
這裡寫圖片描述

Servlet自動載入

預設情況下,第一次訪問servlet的時候建立servlet物件。如果servlet的構造方法或init方法中執行了比較多的邏輯程式碼,那麼導致使用者第一次訪問sevrlet的時候比較慢。
改變servlet建立物件的時機: 提前到載入web應用的時候!!!
在LifeDemo中加入<load-on-startup>1</load-on-startup>即可,其中1表示載入的Servlet順序,這個值越大,載入順序越低

<servlet>
    <servlet-name>LifeDemo</servlet-name>
    <servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

啟動Tomcat如下:
這裡寫圖片描述

有參和無參的init方法

GenericServlet有兩個init方法,其中一個是Sun公司實現的,另一個留給開發者自己覆蓋實現。
這裡寫圖片描述
已實現的init方法會呼叫未實現的init()

這裡寫圖片描述

例子:

public class InitDemo extends HttpServlet {

    /**
     * 有引數的init方法
     * 該方法是servlet的生命週期方法,一定會被tomcat伺服器呼叫
     */
    /**
     * 注意:如果要編寫初始程式碼,不需要覆蓋有引數的init方法
     */
//  @Override
//  public void init(ServletConfig config) throws ServletException {
//      System.out.println("有引數的init方法");
//  }

    /**
     * 無引數的init方法
     * 該方法是servlet的編寫初始化程式碼的方法。是Sun公司設計出來專門給開發者進行覆蓋,然後在裡面編寫servlet的初始邏輯程式碼的方法。
     */
    @Override
    public void init() throws ServletException {
        System.out.println("無引數的init方法");
    }   
}

Servlet多執行緒併發問題

servlet物件在tomcat伺服器是單例項多執行緒的。
因為servlet是多執行緒的,所以當多個servlet的執行緒同時訪問了servlet的共享資料,如成員變數,可能會引發執行緒安全問題。
解決辦法:
1)把使用到共享資料的程式碼塊進行同步(使用synchronized關鍵字進行同步)
2)建議在servlet類中儘量不要使用成員變數。如果確實要使用成員,必須同步。而且儘量縮小同步程式碼塊的範圍。(哪裡使用到了成員變數,就同步哪裡!!),以避免因為同步而導致併發效率降低。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * servlet的多執行緒併發問題,模擬多使用者訪問同一個網站地址,統計訪問量
 * @author APPle
 *
 */
public class TheradDemo extends HttpServlet {
    int count = 1;//共享變數

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");

        synchronized (TheradDemo.class) {//鎖物件必須唯一。建議使用類物件
            System.out.println("你現在是當前網站的第"+count+"個訪客");   //執行緒1執行完  , 執行緒2執行
            count++;
        }
    }
}

ServletConfig物件

作用

主要是用於載入servlet的初始化引數。在一個web應用可以存在多個ServletConfig物件(一個Servlet對應一個ServletConfig物件)

如何建立和得到

建立時機: 在建立完servlet物件之後,在呼叫init方法之前建立。
得到物件: 直接從有引數的init方法中得到!!!

ServletConfig的API:

java.lang.String getInitParameter(java.lang.String name) 根據引數名獲取引數值
java.util.Enumeration getInitParameterNames() 獲取所有引數
ServletContext getServletContext() 得到servlet上下文物件
java.lang.String getServletName() 得到servlet的名稱

例子

web.xml:

  <servlet>
    <servlet-name>ConfigDemo</servlet-name>
    <servlet-class>gz.itcast.f_config.ConfigDemo</servlet-class>
    <!-- 初始引數: 這些引數會在載入web應用的時候,封裝到ServletConfig物件中 -->
    <init-param>
        <param-name>path</param-name><!-- 配置檔案訪問路徑 -->
        <param-value>e:/b.txt</param-value>
    </init-param>
    <init-param>
        <param-name>BBB</param-name>
        <param-value>BBB's value</param-value>
    </init-param>
     <init-param>
        <param-name>CCCC</param-name>
        <param-value>CCCC's value</param-value>
    </init-param>
  </servlet>
package gz.itcast.f_config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ConfigDemo extends HttpServlet {
    /**
     * 以下兩段程式碼GenericServlet已經寫了,我們無需編寫!!
     */
    /*private ServletConfig config;*/

    /**
     *  1)tomcat伺服器把這些引數會在載入web應用的時候,封裝到ServletConfig物件中 
     *  2)tomcat伺服器呼叫init方法傳入ServletConfig物件
     */
    /*@Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }*/

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /**
         * 讀取servlet的初始引數
         */
        String path = this.getServletConfig().getInitParameter("path");//GenericServlet有個getServletConfig方法

        File file = new File(path);
        //讀取內容
        BufferedReader br = new BufferedReader(new FileReader(file));
        String str = null;
        while( (str=br.readLine())!=null ){
            System.out.println(str);
        }

        //查詢當前servlet的所有初始化引數
        Enumeration<String> enums = this.getServletConfig().getInitParameterNames();
        while(enums.hasMoreElements()){
            String paramName = enums.nextElement();
            String paramValue = this.getServletConfig().getInitParameter(paramName);
            System.out.println(paramName+"="+paramValue);
        }

        //得到servlet的名稱
        String servletName = this.getServletConfig().getServletName();
        System.out.println(servletName);
    }

}

ServletContext物件

作用

ServletContext物件 ,叫做Servlet的上下文物件。表示一個當前的web應用環境。一個web應用中只有一 個ServletContext物件,每個ServletConfig中都有一個ServletContext物件。

如何建立和得到

建立時機:載入web應用時建立ServletContext物件。//Tomcat伺服器啟動時載入
得到物件: 從ServletConfig物件的getServletContext方法得到(每個ServletConfig中都有一個ServletContext物件。)

核心API

這裡寫圖片描述

例項

  1. 得到web應用路徑
public class ContextDemo1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //1.得到ServletContext物件
        //ServletContext context = this.getServletConfig().getServletContext();
        ServletContext context = this.getServletContext(); //(推薦使用)


        //2.得到web應用路徑  /day10
        /**
         * web應用路徑:部署到tomcat伺服器上執行的web應用名稱
         */
        String contextPath = context.getContextPath();

        System.out.println(contextPath);// /day10


        /**
         * 案例:應用到請求重定向
         */
        response.sendRedirect(contextPath+"/index.html");
    }
}
  1. 得到Web應用的初始化引數

web.xml

    <!-- 配置web應用引數 -->
    <context-param>
        <param-name>AAA</param-name>
        <param-value>AAA's value</param-value>
    </context-param>
    <context-param>
        <param-name>BBB</param-name>
        <param-value>BBB's value</param-value>
    </context-param>
    <context-param>
        <param-name>CCC</param-name>
        <param-value>CCC's value</param-value>
    </context-param>
public class ContextDemo2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //得到ServletContext物件
        ServletContext context = this.getServletContext();

        System.out.println("引數"+context.getInitParameter("AAA"));

        Enumeration<String> enums = context.getInitParameterNames();
        while(enums.hasMoreElements()){
            String paramName = enums.nextElement();
            String paramValue  =context.getInitParameter(paramName);
            System.out.println(paramName+"="+paramValue);
        }
    }

}
  1. 域物件使用

作用是用於儲存資料,獲取資料。可以在不同的動態資源之間共享資料。
這裡寫圖片描述
ServletContext域物件:作用範圍在整個web應用中有效!!!

public class ContextDemo3 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //1.得到域物件
        ServletContext context = this.getServletContext();

        //2.把資料儲存到域物件中
        //context.setAttribute("name", "eric");
        context.setAttribute("student", new Student("jacky",20));
        System.out.println("儲存成功");
    }
}
public class ContextDemo4 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //1.得到域物件
        ServletContext context = this.getServletContext();

        //2.從域物件中取出資料
        //String name = (String)context.getAttribute("name");
        Student student = (Student)context.getAttribute("student");
        //System.out.println("name="+name);

        System.out.println(student);
    }
}

4、轉發和重定向

轉發:

/**
 * 轉發(效果:跳轉頁面)
 * @author APPle
 *
 */
public class ForwardDemo1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        /**
         * 儲存資料到request域物件
         */
        request.setAttribute("name", "rose");


        //轉發    
        /**
         * 注意:不能轉發當前web應用以外的資源。
         */
        /*RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet");
        rd.forward(request, response);*/
        this.getServletContext().getRequestDispatcher("/GetDateServlet").forward(request, response);
    }

}

重定向:

public class RedirectDemo1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /**
         * 儲存資料到request域物件
         */
        request.setAttribute("name", "rose");

        //重定向
        /**
         * 注意:可以跳轉到web應用內,或其他web應用,甚至其他外部域名。
         */
        //response.sendRedirect("/day09/adv.html");
        response.sendRedirect("/day10/GetDataServlet");
    }
}

轉發和重定向的區別:
1)轉發
a)位址列不會改變
b)轉發只能轉發到當前web應用內的資源
c)可以在轉發過程中,可以把資料儲存到request域物件中
2)重定向
a)位址列會改變,變成重定向到地址。
b)重定向可以跳轉到當前web應用,或其他web應用,甚至是外部域名網站。
c)不能再重定向的過程,把資料儲存到request中。

相關文章