Java Web筆記 Servlet總結

##什麼是servlet

Servlet是sun公司提供的一種用於開發動態web資源的技術

  • 其實就是一個java程序,運行在服務器上,用來接收和響應客戶端的HTTP請求
  • 更多的是配合動態資源來使用,當然靜態資源也用到servlet,只不過是Tomcat裏面已經定義好了,一個DefaultServlet(優先級爲1)

##tmocat和servlet的關係

  • tomcat是web應用服務器,是一個servlet/jsp的容器

  • servlet是一種運行在支持java語言例如tomcat服務器上的組件

##servlet的創建和servlet的web.xml配置

  • 創建一個簡單的servlet
    • 實現servlet接口,或者繼承httpservlet
  • servlet的web.xml配置
    • 在webContent/WEB-INF/web.xml中寫入下面的內容
      <!--告訴tomca,應用裏有這個servlet,名字叫做xxxx
      具體的路徑是xxxx-->
      <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.Servlet.Servletdemo</servlet-class>
      </servlet>
      <!-- 註冊servlet的映射 url-pattern地址欄上地址 -->
      <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello</url-pattern>
      </servlet-mapping>
      • 第一個servlet-name的名字可以隨便寫,不過一般與類名相同
      • 第二個servlet-name與第一個的相同
      • servlet-class包名.類名.class
      • url-pattern 以"/"開頭

###url-paren的配置(通配符)

  • 全路徑匹配
    • 以/開頭 例如
      • /a
      • /a/b
  • 路徑匹配 前半段或者後半段
      • 通配符,匹配任意的文字
      • /a/*
      • *.do

###缺省servlet

  • url-pattern僅爲一個/
  • 凡是在web.xml中沒有匹配的servlet-mapping元素的url,都通過servlet來處理
  • 在tomcat的安裝目錄\config\web.xml文件中,註冊了一個org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個Servlet設置爲了缺省Servlet。我們在訪問靜態資源就是在使用這個缺省的servlet

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    ##servlet的訪問過程運行過程

    servlet程序是由web服務器調用,web服務器接收到客戶端的servlet請求後

  • web服務器檢查是否已經裝載並創建了該servlet對象,如果是就執行第4步,否則執行第二部
  • 裝載並創建一個該servlet對象
  • 調用servlet實例對象的init()方法
  • 創建一個用於封裝HTTP請求消息的HttpServletRequest對象和一個代表HTTP響應消息的HttpServletResponse對象,然後調用Servlet的service()方法並將請求和響應對象作爲參數傳遞進去。
  • web應用被重啓或者重新啓動之前,將卸載servlet,在卸載之前調用servlet的destory方法

servlet的訪問過程

  • 根據要早的servlet的url 在web.xml中找到相匹配的url-pattern
  • 根據找到的路徑在對應的servlet-mapping中找到servlet-name
  • 根據servlet-name去找到servlet中的servlet-name
  • 最後找到了對應的servlet.class文件

##servlet接口及其實現類

  • Servlet接口定義了兩個默認實現類,分別是GenericServlet,HttpServlet
  • HttpServlet在實現Servlet接口時,覆蓋了service方法,該方法體內的代碼會自動判斷用戶的請求方式,因此只需要重寫doGet方法或者doPost方法
  • HttpServlet指能夠處理HTTP請求的servlet,在原有的servlet接口上添加了一些HTTP協議的處理方法,比Servlet接口更加強大,因此在編寫Servlet時,通常繼承這個類,而避免直接去實現Servlet接口

###爲什麼要有兩個接口

  • GenericServlet雖然實現了Servlet接口,但是沒有指定所實用的協議,而HttpServlet指定了所使用的HTTP協議,SUN公司處於維護的目的,一旦出現了別的協議,就可以直接添加新的類似HttpServlet的類繼承GenericServlet
servlet(接口)
GenericServlet(重寫了5個接口)

httpservlet(用於處理http的請求)

##servlet的生命週期
###生命週期

  • 從創建到銷燬的時間

###生命週期方法

  • 從創建到銷燬所調用的方法,都叫生命週期方法
  • inti()方法
    • 在創建servlet的實例時會調用init方法
    • 聲明時候創建實例
      • 默認情況下,初次方法是時候會創建實例
      • 一個servlet只會初始化一次,也就是說init只會執行一次,servlet是一個單實例,多線程的類,只會創建一次
  • service 方法

    • 只要客戶端來了一個請求,就會調用這個方法
    • 該方法可以被執行多次,來一次請求就調用service方法
  • destory方法
    • 銷燬的時候就執行該方法
    • 什麼時候銷燬
      • 該項目從tomcat中移除
      • 正常的關閉服務器

doGet和doPost不算生命週期方法,生命週期方法只指,從對象創建到銷燬一定會執行的方法,但這兩個方法不一定會執行

###創建的時機,前提

  1. 默認情況下,只有在初次訪問servlet的時候,纔會執行init方法,有的時候,我們需要在這個方法裏面執行一些初始化工作,甚至做一些比較耗時的邏輯
  2. 那麼這個時候,初次訪問,就可能在init方法中逗留太久時間,那麼有沒有方法讓初始化的時間提前一點
  3. 在配置的時候,使用load-on-starup來指定,給定的數字越小,啓動的時間就越早,一般不寫負數,從2開始,因爲1已經被tomcat裏的東西所佔用
        <servlet>
            <servlet-name>demo</servlet-name>
            <servlet-class>com.Servlet.Demo</servlet-class>
            <load-on-startup>2</load-on-startup>
        </servlet>
  • load-on-startup
    • 標記容器是否在啓動的時候就加載這個servlet
    • 當值爲非負數的時候就在啓動時就加載servlet
    • 當時一個負數或者沒有指定時,該servlet在選擇時加載
    • 正數的值越小,啓動servlet的優先級越高
    • 0比1的優先級高
    • 我們一般從2開始,1是tomcat的servlet的優先級

##servlet的線程安全問題

當多個tomcat併發的訪問同一個servlet時,web服務器爲每一個客戶端的訪問請求創建一個線程,並在這個線程上調用servlet的service方法,如果service方法訪問了同一個資源的話就看你引發多線程安全問題

  • (假)解決方法,加鎖保證任何時候都只有一個線程在方法servlet中的資源
    • 這樣雖然解決了線程安全問題,但是很不現實,因爲這樣就變成了輪流訪問了
  • (過時)sun公司提供的解決方案,讓Servlet去實現一個SingleThreadModel接口,如果某個Servlet實現了SingleThreadModel接口,那麼Servlet引擎將以單線程模式來調用其service方法。
    • 對於實現了SingleThreadModel接口的Servlet,Servlet引擎仍然支持對該Servlet的多線程併發訪問,其採用的方式是產生多個Servlet實例對象,併發的每個線程分別調用一個獨立的Servlet實例對象。
    • 實現SingleThreadModel接口並不能真正解決Servlet的線程安全問題,因爲Servlet引擎會創建多個Servlet實例對象,而真正意義上解決多線程安全問題是指一個Servlet實例對象被多個線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記爲Deprecated(過時的)。
    • 創建多個對象會佔用服務器資源

##servletConfig
###配置servletconfig

  • 在web.xml中配置
<servlet>
    <servlet-name>ServletConfigDemo1</servlet-name>
    <servlet-class>study.ServletConfigDemo1</servlet-class>
    <!--配置ServletConfigDemo1的初始化參數 -->
    <init-param>
        <param-name>name</param-name>
        <param-value>gacl</param-value>
    </init-param>
     <init-param>
        <param-name>password</param-name>
        <param-value>123</param-value>
    </init-param>
    <init-param>
        <param-name>charset</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</servlet>

###獲取servletconfig

  • 通過初始化的方法獲得servletconfig對象
public void init(ServletConfig config) throws ServletException {
    this.config = config;
}
  • 通過父類GenericServlet的方法獲得一個ServletConfig對象
ServletConfig config = getServletConfig()

###獲取配置的初始化參數

servlet配置了初始化參數後,web容器在創建servlet實例對象時,會自動將初始化參數封裝到servletConfig對象中,在調用init方法時,會將servletConfig對象傳遞給servlet

  • 例子
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //獲取在web.xml中配置的初始化參數
        String paramVal = this.config.getInitParameter("name");//獲取指定的初始化參數
        response.getWriter().print(paramVal);

        response.getWriter().print("<hr/>");
        //獲取所有的初始化參數
        Enumeration<String> e = config.getInitParameterNames();
        while(e.hasMoreElements()){
            String name = e.nextElement();
            String value = config.getInitParameter(name);
            response.getWriter().print(name + "=" + value + "<br/>");
        }
    }

###servletchongfig的方法

  • getServletName() 獲取servlet的名稱,就是在web.xml文件中配置的
  • getservletContext() 獲取servletcontext對象
  • getInitParameter(String) 獲取在Servlet中初始化的參數的值,只能獲取該Servlet下面的值
  • getInitParameterNames() 獲取該servlet中所有初始化參數的名字,根據名字就可以獲得各個初始化參數的value值,返回的類型是枚舉類型
    值的注意的是我們可以直接調用getServletName()等方法來獲取個種參數,因爲在父類GenericServlet中,已經幫我們獲取了這些數據,不在需要我們手動調用了

###servletconfig的作用

  1. 未來我們自己開發的一些應用,使用了一些技術,或者一些代碼,我們不會,但有人寫出來了,天的代碼放置在了自己的servlet類中
    剛好這個servlet裏面需要一個數字或者變量值,但這個值不能是固定的,所以要求使用這個servlet的公司,在註冊servlet的時候,必須在web.xml裏面,聲明init-params
  2. 存放一些數據庫的連接啊,用戶名密碼啊,修改的時候就可以不修改源代碼,只修改配置文件就夠了

##servletcontext
###什麼是servletcongtext

有叫上下文,域對象,簡單的說一個web項目有一個servletcontext實例,每個servlet都可以訪問他

  • web容器在啓動時,會爲每個web應用都創建對應的servlet實例,在服務器啓動時創建,在關閉時銷燬,在一個web項目中共享數據,管理web項目資源
  • 由於一個web應用中所有servlet共享了一個servletcontext對象,因此servlet對象之間可以通過servletcontext來實現通訊,因此有被稱爲域對象

###獲取servletcontext

  • 直接用父類的方法getServletContext()
  • 通過servletconfig來獲取servletcontextgetServletConfig().getServletContext();

###配置參數

  • 必須在web.xml的最上方
<contxet-param>
    <param-name>add</param-name>
    <param-value>123</param-value>
</context-param>
  • 是一組鍵值對 name是鍵,value是值
  • 當服務啓動的時候,會讀取web.xml文件,當讀到<context-param>這個節點,就set到我們的servletcontext中,我們就可以通過servletcontext來獲取全局配置信息

###作用
####實現數據共享

  • 在當前應用,是servlet共享數據
    • setAttribute(String name,Object obj) 在servletcontext中存入內容
    • getAttribute(String name) 通過名稱得到內容
    • removeAttribute(String name) 移除名稱的內容

####獲取配置的參數

  • 通過方法getInitParameter("add")獲取
    String add = servletContext.getInitParameter("add");
  • 通過getInitParameters() 獲取枚舉類型

####實現請求轉發

ServletContext context = this.getServletContext();//獲取ServletContext對象
RequestDispatcher rd = context.getRequestDispatcher("要去的界面");//獲取請求轉發對象(RequestDispatcher)
rd.forward(request, response);//調用forward方法實現請求轉發

####讀取資源文件--獲得路徑

  • 獲取web項目下指定資源的路徑
    直接調用getServltContext().getRealPath("");獲得到
    D:\apache-tomcat-9.0.12\wtpwebapps\項目名\
  • 想獲得webcontent的下文件直接加文件名就好了
getServltContext().getRealPath("文件名")
  • 獲得Java Resoureces --> src下的文件
getServltContext().getRealPath("/WEB-INF/classes/文件名")
  • 獲得src--->db.config包中的文件
getServletContext().getRealPath("/WEB-INF/classes/db/config/文件名");

爲什麼在classes下呢

  • tomcat會把web工程下的java文件編譯後的字節碼文件.class放到classes下,會把其他的資源也存放到classes下

####讀取資源文件--獲得流

  • 將獲得路徑的方法替換爲獲得流的方法
  • getResourceAsStream 獲取資源的流對象

####讀取資源文件--class類加載器

  • 注意事項:不適合裝載大文件,否則會導致jvm內存溢出
  • 類加載器得到的路徑D:\apache-tomcat-8.5.33\wtpwebapps\ServletRegister\WEB-INF\classes
  • 得到src下的文件
        //獲取到裝載當前類的類裝載器
        ClassLoader loader = ServletContextDemo1.class.getClassLoader();
        //用類裝載器讀取src目錄下的文件
        InputStream in = loader.getResourceAsStream("文件名");
  • 得到包下的資源文件
        //獲取到裝載當前類的類裝載器
        ClassLoader loader = ServletContextDemo1.class.getClassLoader();
        //用類裝載器讀取src目錄下的servlet.study包中的文件
        InputStream in = loader.getResourceAsStream("servlet/study/db4.properties");
  • 得到webcontent下的資源文件,先向上返回兩層到工程目錄下,在進去想得到的文件
        //獲取到裝載當前類的類裝載器
        ClassLoader loader = ServletContextDemo1.class.getClassLoader();
        //用類裝載器讀取webContent下的文件
        classLoader.getResourceAsStream("../../Config.properties");

###控制瀏覽器緩存

添加Expires頭可以有效減少Http請求次數,只要瀏覽器中有緩存,並且相應的資源未過期,均可以使用緩存中的資源,無須再發送請求到服務器

public class ResponseDemo6 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /**
         * 設置數據合理的緩存時間值,以避免瀏覽器頻繁向服務器發送請求,提升服務器的性能
         * 這裏是將數據的緩存時間設置爲1小時
         */
        response.setDateHeader("expires", System.currentTimeMillis()+1000*3600);
        String data = "aaaaaaaaaa";
        response.getWriter().write(data);
    }

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

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章