【Servlet教科書】Servlet基礎總結(詳細記錄知識)

文章目錄

一、初識Servlet

1.1 Servlet概念

Servlet(Server Applet)是Java Servlet的簡稱,稱爲小服務程序或服務連接器,用Java編寫的服務器端程序,具有獨立於平臺和協議的特性,主要功能在於交互式地瀏覽和生成數據,生成動態Web內容。 簡單來說,Servlet是服務器端的一段程序(代碼、功能實現),可交互式的處理客戶端發送到服務器的請求,並完成操作響應。並支持動態網頁技術。JavaWeb程序開發的基礎,JavaEE規範(一套接口)的一個組成部分。它是由服務器廠商實現的。

1.2 Servlet的核心作用

  1. 接受客戶端請求,完成操作任務
  2. 動態生成網頁(頁面數據可變)
  3. 將包含操作結果的動態網頁響應給客戶端

1.3 Servlet核心目錄結構

web :存放需要部署的網站項目

——WEB-INF :核心內容,分別是以下內容

———classes :存放.class文件(XxxServlet.class)

———lib :儲存所需jar包

———web.xml :web配置文件

——index.html/index.jsp.index.css/images等

見idea目錄結構如下圖: (因爲idea會自動處理部署的文件並打包成war包的形式儲存在out文件中,所以我們在使用IDEA時不用自己創建classes文件)

在這裏插入圖片描述

1.3 IDEA工具內創建核心目錄結構

因爲我們使用的是idea,如果去項目目錄創建該Servlet目錄結構過於繁瑣,所以我們可以使用idea在工具內創建目錄結構(可以在配置tomact時提前創建好都是OK的!)

在這裏插入圖片描述

1.4 Servlet的開發步驟

1.4.1 開發步驟
  1. 搭建開發環境,並創建Servlet核心目錄結構
  2. 實現javax.serlvet.Servlet接口,覆蓋5個方法(我在1.4.3裏的每個方法上做了詳細的註釋)
  3. 在覈心的servlet()方法中書寫輸出語句,驗證訪問結果
  4. 將編譯後的.class文件放置在WEB-INF/classes中
  5. web.xml文件中添加配置信息

注意:

Java web工程下的web就是工程的發佈文件夾,發佈時會把該文件夾發佈到idea項目下的out文件夾裏artifacts。開發時classes文件存放路徑爲out文件夾下production文件夾下

鑑於我們使用的是IDEA集成開發工具,會自動處理文件放在idea並放在production文件夾內;而Eclipse不同,他會自動處理文件並放在tomact容器中的指定文件夾內,所以開發步驟作爲了解,但是也可以自己動手試試!(後面我貼了一張圖,可以去看一下!至於關於war包的情況不瞭解,可以去翻看tomact教科書系列文章)

在使用DOS命令行去編譯執行Web項目時,我們需要將Servlet相關的jar包 完整路徑\lib\servlet-api.jar 配置到CLASSPATH中。)

如果配置了環境還出現引用包的問題,那我們就可以選擇帶包編譯或者不帶包編譯了。如下:

帶包編譯:javac -d . -classpath D:\tomcat\apache-tomcat-8.5.45\lib\servlet-api.jar MyServlet.java

不帶包編譯: javac -classpath D:\apache-tomcat-7.0.42\lib\servlet-api.jar MyServlet.java

在這裏插入圖片描述

1.4.2 web.xml配置文件添加配置信息

解釋放在了配置信息中,你們看註釋即可理解!

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
	<!--下面兩個標籤,寫在web-app標籤內-->
	<!--Servlet配置-->
    <servlet>
        <!--Servlet的全稱類名,通過名稱找到對應的Servlet,因爲的配置文件中可能存在很多Servlet,他需要一個可識別的名稱標籤-->
        <servlet-name>myservlet</servlet-name>
        <!--訪問實際的類,這裏需要寫全限定名-->
        <servlet-class>com.mylifes1110.java.MyServlet</servlet-class>
    </servlet>
	<!--映射配置  -->
    <servlet-mapping>
        <!--同上,Servlet名稱-->
        <servlet-name>myservlet</servlet-name>
        <!--URL路徑訪問名稱,比如:localhost:8080/firstservlet/test(這裏訪問就需要在地址欄上假如test)-->
        <url-pattern>/test</url-pattern>
    </servlet-mapping>
</web-app>
1.4.3 我們的第一個Servlet(MyServlet代碼)

實現javax.serlvet.Servlet接口,覆蓋5個方法,標有詳細註釋

package com.mylifes1110.java;

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

/**
 * Servlet
 * 實現Servlet接口中的所有方法
 */
public class MyServlet implements Servlet {
    
    /**
     * 實例化(調用構造器,可以省略,但是要知道)
     */
    public MyServlet() {
        //默認無參構造
    }
    
    /**
     * 初始化方法
     *
     * @param servletConfig 包含 servlet 的配置和初始化參數的 ServletConfig 對象
     * @throws ServletException 如果發生妨礙 servlet 正常操作的異常
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        //Servlet初始化工作
    }

    /**
     * 獲取Servlet配置信息
     *
     * @return 返回 ServletConfig 對象,該對象包含此 servlet 的初始化和啓動參數。返回的 ServletConfig 對象是傳遞給 init 方法的對象。
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服務
     * <p>
     * 由 servlet 容器調用,以允許 servlet 響應某個請求。
     * 此方法僅在 servlet 的 init() 方法成功完成之後調用。
     * 應該爲拋出或發送錯誤的 servlet 設置響應的狀態代碼。
     *
     * @param servletRequest  包含客戶端請求的 ServletRequest 對象
     * @param servletResponse 包含 servlet 的響應的 ServletResponse 對象
     * @throws ServletException 如果發生妨礙 servlet 正常操作的異常
     * @throws IOException      果發生輸入或輸出異常
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //請求相關內容    ServletRequest  ;   相應相關內容  ServletResponse
        /**
         * 在控制檯內打印輸出
         */
        System.out.println("這是我的第一個Servlet!");

        /**
         * 利用輸出流輸出系統時間,在瀏覽器中顯示
         */
        PrintWriter printWriter = servletResponse.getWriter();
        Date date = new Date();
        printWriter.println(date);
        printWriter.close();

        /**
         * 利用流輸出信息在瀏覽器內顯示
         * 解決瀏覽器顯示亂碼問題
         */
        servletResponse.setContentType("text/html;charset=utf-8");
        servletResponse.getWriter().println("這是我的第一個Servlet");

    }

    /**
     * 返回有關 servlet 的信息,比如作者、版本和版權。
     * <p>
     * 此方法返回的字符串應該是純文本,不應該是任何種類的標記(比如 HTML、XML,等等)。
     *
     * @return 包含 servlet 信息的 String
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 銷燬(清除所有資源)
     * <p>
     * 由 servlet 容器調用,指示將從服務中取出該 servlet。
     * 此方法僅在 servlet 的 service 方法已退出或者在過了超時期之後調用一次。
     * 在調用此方法之後,servlet 容器不會再對此 servlet 調用 service 方法。
     * 此方法爲 servlet 提供了一個清除持有的所有資源(比如內存、文件句柄和線程)的機會,並確保任何持久狀態都與內存中該 servlet 的當前狀態保持同步。
     */
    @Override
    public void destroy() {

    }
}

地址欄輸入:http://localhost:8080/firstservlet/test (firstservlet是項目資源名稱、test是Servlet名稱)

1.4.4 解決瀏覽器輸出顯示亂碼問題
		/**
         * 利用流輸出信息在瀏覽器內顯示
         * 解決瀏覽器顯示亂碼問題
         */
        servletResponse.setContentType("text/html;charset=utf-8");
        servletResponse.getWriter().println("這是我的第一個Servlet");
1.4.5 解決idea控制檯亂碼

如果還解決不了,參考文章有多種解決方法:https://blog.csdn.net/weixin_44170221/article/details/105478788

-Dfile.encoding=utf-8

在這裏插入圖片描述

1.5 Web中路徑問題

1.5.1 路徑分類

關於路徑分類有這麼幾種:絕對路徑相對路徑根路徑

1.5.2 路徑分析

絕對路徑 :用在不同網站之間跳轉,比如:http://www.baidu.com.image/sky.png

相對路徑 :用在同一網站中,比如:image/1.jpg,僅限靜態資源,如果頁面比較多,並且使用框架,會出現混亂

根路徑 :根指定就是主機名(服務器)

比如:/servletdemo/loginservlet,如果在瀏覽器中,/ 表示主機名http://localhost:8080/

比如:/loginservlet,如果在服務器中,/ 表示項目路徑/servletdemo

二、Servlet的使用

2.1 Servlet核心接口和類

關於使用Servlet有三種方法: 實現Servlet接口、繼承GenericServlet 抽象類、繼承HttpServlet抽象類

2.1.1 實現Servlet接口(繁瑣)

關於實現Servlet接口覆蓋其所有方法,其實在上面的1.4.3章節中已經寫過了,但是我們現在再寫一遍!

package com.mylifes1110.java;

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

/**
 * Servlet
 * 實現Servlet接口中的所有方法
 */
public class MyServlet implements Servlet {
    /**
     * 初始化方法
     *
     * @param servletConfig 包含 servlet 的配置和初始化參數的 ServletConfig 對象
     * @throws ServletException 如果發生妨礙 servlet 正常操作的異常
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        //Servlet初始化工作
    }

    /**
     * 獲取Servlet配置信息
     *
     * @return 返回 ServletConfig 對象,該對象包含此 servlet 的初始化和啓動參數。返回的 ServletConfig 對象是傳遞給 init 方法的對象。
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服務
     * <p>
     * 由 servlet 容器調用,以允許 servlet 響應某個請求。
     * 此方法僅在 servlet 的 init() 方法成功完成之後調用。
     * 應該爲拋出或發送錯誤的 servlet 設置響應的狀態代碼。
     *
     * @param servletRequest  包含客戶端請求的 ServletRequest 對象
     * @param servletResponse 包含 servlet 的響應的 ServletResponse 對象
     * @throws ServletException 如果發生妨礙 servlet 正常操作的異常
     * @throws IOException      果發生輸入或輸出異常
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //請求相關內容    ServletRequest  ;   相應相關內容  ServletResponse
        /**
         * 在控制檯內打印輸出
         */
        System.out.println("這是我的第一個Servlet!");

        /**
         * 利用輸出流輸出系統時間,在瀏覽器中顯示
         */
        PrintWriter printWriter = servletResponse.getWriter();
        Date date = new Date();
        printWriter.println(date);
        printWriter.close();

        /**
         * 利用流輸出信息在瀏覽器內顯示
         * 解決瀏覽器顯示亂碼問題
         */
        servletResponse.setContentType("text/html;charset=utf-8");
        servletResponse.getWriter().println("這是我的第一個Servlet");

    }

    /**
     * 返回有關 servlet 的信息,比如作者、版本和版權。
     * <p>
     * 此方法返回的字符串應該是純文本,不應該是任何種類的標記(比如 HTML、XML,等等)。
     *
     * @return 包含 servlet 信息的 String
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 銷燬(清除所有資源)
     * <p>
     * 由 servlet 容器調用,指示將從服務中取出該 servlet。
     * 此方法僅在 servlet 的 service 方法已退出或者在過了超時期之後調用一次。
     * 在調用此方法之後,servlet 容器不會再對此 servlet 調用 service 方法。
     * 此方法爲 servlet 提供了一個清除持有的所有資源(比如內存、文件句柄和線程)的機會,並確保任何持久狀態都與內存中該 servlet 的當前狀態保持同步。
     */
    @Override
    public void destroy() {

    }
}

我們看到了實現Servlet接口的方法來使用Servlet。其中我們可以看到5個方法,分別是初始化、獲取配置、提供服務、返回信息以及銷燬。而我們有沒有發現5個方法中有對我們來說有不是必須全部寫在裏面的方法呢?或者是可以優化,把某個方法封裝來實現複用呢?其實有的,5個方法中初始化和銷燬我們可以封裝一下,實現多個Servlet之間複用。而獲取配置、返回信息感覺是沒有必要的。所以我們引入了GenericServlet抽象類。那麼繼承這個抽象類有什麼好處呢?會比我們實現Servlet接口更簡單化嗎?繼續向下看吧那就!

2.1.2 繼承GenericServlet 抽象類(無協議)

GenericServlet的內部也實現了Servlet接口,並重寫了初始化、獲取配置、返回信息、銷燬方法,有沒有發現少了一個提供服務呢?它的做法很聰明,重寫了我們想要簡化的那四個方法並返回了默認沒有的值(空值等),這就相當於我們實現了Servlet接口,並沒有給他們編寫方法體代碼而已。那麼少的那個提供服務,它做了什麼呢?它的做法是把提供服務的方法用abstract修飾成了抽象類,所以我們繼承這個類必須覆蓋該抽象方法。

以上所述,GenericServlet 使編寫 Servlet 變得更容易。它提供生命週期方法 init 和 destroy 的簡單實現,要編寫一般的 Servlet,只需重寫抽象 service 方法提供服務即可。 (那麼這個代碼就簡化了太多了,看代碼吧!)

package com.mylifes1110.java;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * 繼承GenericServlet抽象類並重寫service方法提供服務
 */
public class MyServlet2 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("提供服務!");
    }
}

那麼我說的你們不信的話,我給你們再貼一下源碼,你們可以看看,或者覺得透着些。

在這裏插入圖片描述

destroy銷燬方法

在這裏插入圖片描述

getServletConfig獲取配置方法(this.config在最上面聲明是這樣聲明空值的:private transient ServletConfig config;

在這裏插入圖片描述

getServletInfo返回信息方法

在這裏插入圖片描述

init初始化方法

在這裏插入圖片描述

然而主角一般都是最後登場,那就是service提供服務方法被寫爲抽象方法

在這裏插入圖片描述

這樣來,我們不需要那4個方法時,就可以直接覆蓋service方法去提供服務,假如需要某一個方法時,我們可以直接選擇性的重寫父類方法即可!然而小夥伴們有沒有想過,GenericServlet只是定義了一般的、與協議無關的servlet,而編寫基於Web上的HTTP協議下的servlet,所以GenericServlet與協議無關是不安全的,鑑於目前HTTP和HTTPS的廣泛使用,市面上很少有Web不使用HTTP協議的。那麼這個問題來的這麼突然,我們怎麼辦呢?有沒有更好的使用Servlet的方法又安全、又簡單化呢?答案很明顯,在上面我介紹了三種使用Servlet的方法,現在只介紹了兩種,那麼答案顯而易見就是第三種方法了。於是,我們強大的HttpServlet抽象類登場了!(我發現主角總是最後登場的,哈哈!)

2.1.3 繼承HttpServlet抽象類(有協議,推薦使用)

HttpServlet抽象類的內部繼承了GenericServlet抽象類(證明HttpServlet有了上面我們說到的那四個方法),並擴展提供了適用於Web站點的HTTP協議的幾個方法,也就是說HttpServlet的子類至少必須重寫一個方法,該方法通常是如下這幾個方法:

  • doGet,如果 servlet 支持 HTTP GET 請求
  • doPost,用於 HTTP POST 請求
  • doPut,用於 HTTP PUT 請求
  • doDelete,用於 HTTP DELETE 請求
  • initdestroy,用於管理 servlet 的生命週期內保存的資源
  • getServletInfo,servlet 使用它提供有關其自身的信息

注意:因爲我們是繼承了GenericServlet抽象類,所以沒有理由再去重寫 service 方法。service 通過將標準 HTTP 請求分發給每個 HTTP 請求類型的處理程序方法(上面列出的 doXXX 方法)來處理它們。(只需要使用擴展的Http的所需方法即可)

package com.mylifes1110.java;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 繼承HttpServlet抽象類
 */
public class MyServlet3 extends HttpServlet {
    /**
     * doGet    HTTP中Get請求
     * doPost   HTTP中Post請求
     */

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doGet(req, resp);
        /**
         * 當接受參數時,我們不使用Get請求,就在內部調用doPost方法,這樣傳來參數後,就避免了使用Get請求方式(默認使用一個請求方式)
         */
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       	resp.getWriter().println("這是我的第一個Servlet!");
    }
}

那我們也看一下源碼是怎麼實現的!

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

看到這裏,我們上面說到,爲什麼沒有理由去重寫service方法呢,在這裏我們去做解釋!看源碼!

在這裏插入圖片描述

你會發現這個service方法參數是父類請求、響應的參數。而傳入參數後做了強轉爲子類參數進行使用(因爲父類參數是不可以使用的,我們使用的是關於Http的參數),又調用了this.service方法,我們會想這不是自己調用自己陷進去一種死循環調用嗎?然而源碼內還提供了一個傳入子類參數的service方法。如下:

在這裏插入圖片描述

在上面的那個service方法中this.service(request, response); 這裏我們調用的是哪一個service方法呢?然後這裏我們就會想到了JavaSE中的多態,一個傳入父類參數的service,還有一個傳入子類參數的service方法。至於選擇哪一個,就看我們傳入的參數了。它會精確匹配參數類型而選擇傳入哪一個service方法中。而這個傳入子類參數的service方法內做了什麼呢?看我標紅的段落,傳入請求參數後,調用Method方法,如果調用方法後得到的是GET,那就會調用doGet方法,而doGet方法就是上面我們說到的HTTP中的Get請求!舉一反三,其他的Post、Delete等都是如此!

在這裏插入圖片描述

也許就有人會問Method方法是什麼,那我正好就粘出來,你們就明白了!(返回一個字符串!)

2.2 Servlet的兩種配置方式

Servlet的兩種配置方式爲web.xml配置方式和註解配置方式

2.2.1 web.xml配置方式(支持所有版本)

容器在進行url-pattern配置的時候是遵循一定的匹配原則的

url-pattern定義匹配規則,取值說明如下表:

url-pattern配置名稱 配置說明 取值說明
/具體名稱 精確匹配 只有url路徑是具體名稱的時候纔會觸發Servlet
*.xxx 後綴匹配 只要是以xxx結尾的就匹配成功並觸發Servlet
/a/b/* 目錄匹配 訪問時必須書寫/a/b/*路徑,*的話隨便輸入(必須以"/“開頭,以”*"結尾)
/* 通配符匹配 匹配所有請求,包含服務器的所有資源
/ 通配符匹配 匹配所有請求,包含服務器的所有資源,不包括 .jsp

load-on-startup

  1. 元素標記容器是否應該在web應用程序啓動的時候就加載觸發Servlet初始化
  2. 它的值必須是一個整數,表示Servlet被加載的先後順序
  3. 如果該元素的值是負數或者沒有設置,則容器會當Servlet被請求時在加載
  4. 如果值爲正整數或者0時,表示容器在應用程序啓動時就加載並觸發了Servlet的初始化。(值越小,Servlet的優先級越高,越先被加載。值相同時,容器自己選擇順序加載)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--Servlet配置-->
    <servlet>
        <!--Servlet屬性名稱-->
        <servlet-name>myservlet</servlet-name>
        <!--全限定名路徑-->
        <servlet-class>com.mylifes1110.java.MyServlet</servlet-class>
        <!--啓動時被加載並初始化-->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <!--映射配置-->
    <servlet-mapping>
        <!--Servlet屬性名稱-->
        <servlet-name>myservlet</servlet-name>
        <!--精確匹配-->
        <url-pattern>/test</url-pattern>
        <!--後綴匹配-->
        <url-pattern>*.myservlet</url-pattern>
        <!--通配符匹配-->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>
2.2.2 註解類WebServlet配置方式(支持3.0版本以後)

@WebServlet有4個屬性我們目前可以使用分別是name、value、urlPatterns和loadOnStartup

註解屬性 類型 屬性說明
name String 指定Servlet 的 name 屬性,等價於 <servlet-name>。如果沒有顯式指定,則該 Servlet 的取值即爲類的全限定名
value String[] 配置url路徑(idea爲我們設置路徑提供了兩個屬性,根據自己習慣去用)
urlPatterns String[] 配置url路徑,與value作用相同,不能同時使用
loadOnStartup int 配置Servlet的創建的時機, 如果是0或者正數啓動程序時,則創建,如果是負數,則訪問時創建
initParams WebInitParam[] 指定一組 Servlet 初始化參數,等價於<init-param>標籤。
asyncSupported boolean 聲明 Servlet 是否支持異步操作模式,等價於<async-supported> 標籤
description String 該 Servlet 的描述信息,等價於 <description>標籤
displayName String 該 Servlet 的顯示名,通常配合工具使用,等價於 <display-name>標籤
package com.mylifes1110.java;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * value定義了多個url路徑:value = {"/hi", "/hello", "*.haha"}
 * Servlet的創建時機設置爲:loadOnStartup = 0(啓動程序時創建並初始化)
 */
@WebServlet(name = "HiServlet", value = {"/hi", "/hello", "*.haha"}, loadOnStartup = 0)
public class HiServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("初始化方法");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("你又被訪問到了!哈哈!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}
2.2.3 IDEA中快捷創建Servlet類並自動添加註解

IDEA中爲爲我們提供了比較快捷的方式來創建一個Servlet類,並自動的添加了@WebServlet註解,而且還覆蓋了doPost和doGet方法,如下操作

在這裏插入圖片描述

在這裏插入圖片描述

創建後類中顯示如下內容,沒有騙你吧。IDEA是不是很強大,很方便!

在這裏插入圖片描述

2.2.4 Servlet默認訪問歡迎頁面配置

大家都知道,如果在訪問項目的默認路徑時,會出現默認的訪問頁面的,默認訪問的頁面是index.html、index.htm、index.jsp。這個歡迎頁面我們是可以手動干預的,可以利用配置來進行設置我們的歡迎頁面顯示!我把代碼貼在下方,大家可以去試試!

	<!--默認訪問頁面設置-->
    <!--隨便設置訪問頁面,按順序默認訪問,如果默認設置的頁面都沒有的話,就會出現404(找不到資源)-->
    <welcome-file-list>
        <welcome-file>歡迎.html</welcome-file>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

歡迎頁面內的文件是順序被默認訪問的,也就是說如果沒有歡迎.html,瀏覽器會默認訪問第二個index.html,如果第二個也沒有,那就訪問第三個index.jsp,如果三個頁面都不存在的話,那事情就大了,默認訪問會找不到資源,這時我們的老朋友404就出現了!

這麼想,如果客戶端在訪問的時候,資源不見了出現404顯得過於生硬,感覺不太友好!這時候我們就可以使用<error-page>配置標籤來進行錯誤頁面友好提示的默認訪問設置!也就是說如果客戶端訪問資源不存在,我們把客戶端遇到的404問題,再跳轉一個友好處理404頁面的網頁!怎麼設置,那就看一下2.2.5的知識點吧!

2.2.5 Servlet訪問狀態碼跳轉頁面設置

上面那個不友好顯示的問題,我們可以再此處得到解決。友好的提示客戶端,你的訪問狀態碼對應的錯誤提示!可以加入如下配置:

	<!--注意:我們也可以設置其他訪問狀態碼比如500、302等的跳轉頁面-->
    <!--訪問狀態碼跳轉頁面設置-->
    <error-page>
        <!--訪問狀態碼-->
        <error-code>404</error-code>
        <!--跳轉頁面路徑-->
        <location>/error/error404.html</location>
    </error-page>

注意:跳轉頁面的訪問路徑我是在web文件夾中創建了一個error文件夾專門存放處理訪問狀態碼後跳轉頁面(該項目中處理404的頁面是error404.html)的倉庫

2.3 獲取請求參數

html頁面代碼如下:

注意:當不寫method方法時,默認時get請求(如果對get/post請求不瞭解的,可參考HTTP教科書系列)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form method="post" action="/firstservlet/login">
    用戶名:<input type="text" name="username"><br>
    密碼:<input type="password" name="password"><br>
    <input type="submit" value="登錄">
</form>
</body>
</html>

Servlet代碼如下:

package com.mylifes1110.java.login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 獲取請求參數並響應結果
 */
@WebServlet(name = "LoginServlet", value = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * 解決獲取瀏覽器Post請求後在控制檯中解碼後的亂碼問題
         */
        request.setCharacterEncoding("utf-8");
        /**
         * 獲取瀏覽器發送的請求
         */
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username : " + username + "\t" + "password : " + password);
        /**
         * 解決服務器響應瀏覽器後瀏覽器顯示內容亂碼問題
         */
        response.setContentType("text/html;charset=utf-8");
        /**
         * 根據傳來的用戶名和密碼響應瀏覽器登錄成功或登錄失敗
         */
        if (username.equals("ziph") && password.equals("123456")) {
            response.getWriter().println("登錄成功!");
        } else {
            response.getWriter().println("登錄失敗!");
        }
    }
}

2.4 處理獲取請求參數亂碼問題

2.4.1 表單中產生中文亂碼的原因

亂碼問題,實質上就是因爲服務器和客戶端交互之間編碼不一致造成的,所以我們解決亂碼問題肯定根據實質出發,把服務器和客戶端交互過程中的編碼統一不就好了嗎,之後就按照此編碼進行數據的傳輸與接收。

2.4.2 解決接收Get請求參數亂碼問題

在tomact7版本及之前版本

客戶端以UTF-8的編碼傳輸數據到服務器端,而服務器端的request對象使用的是ISO-8859-1這個字符編碼來接收數據,服務器和客戶端溝通的編碼不一致因此纔會產生中文亂碼的。解決辦法:在接收到數據後,先獲取request對象以ISO8859-1字符編碼接收到的原始數據的字節數組,然後通過字節數組以指定的編碼構建字符串,解決亂碼問題。

Tomcat8的版本及之後Get基本就不會亂碼了,因爲服務器對url的編碼格式可以進行自動轉換

package com.mylifes1110.java.login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 處理接受Get請求亂碼問題
 * 注意:tomact7版本及以前纔會出現,Tomact8已經做出優化!
 */
@WebServlet(name = "GetServlet", value = "/login")
public class GetServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //獲取用戶名
        String username = request.getParameter("username");
        username = new String(username.getBytes("ISO8859-1"), "UTF-8");
        //獲取密碼
        String password = request.getParameter("password");
        System.out.println("username : " + username + "password : " + password);
    }
}
2.4.3 解決接收Post請求參數亂碼問題

由於客戶端是以UTF-8字符編碼將表單數據傳輸到服務器端的,因此服務器也需要設置以UTF-8字符編碼進行接收,要想完成此操作,服務器可以直接使用從ServletRequest接口繼承而來的"setCharacterEncoding(charset)"方法進行統一的編碼設置。

package com.mylifes1110.java.login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 處理接收Post請求亂碼問題
 * 注意:此設置請求參數的編碼格式對Get請求方式無效
 */
@WebServlet(name = "PostServlet")
public class PostServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解決接收Post請求參數亂碼問題————設置請求參數的編碼格式(Get無效)
        request.setCharacterEncoding("utf-8");
        //獲取用戶名
        String username = request.getParameter("username");
        //獲取密碼
        String password = request.getParameter("password");
        System.out.println("username : " + username + "password : " + password);
    }

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

2.5 處理響應後瀏覽器顯示亂碼問題

2.5.1 響應後瀏覽器頁面顯示亂碼的原因

瀏覽器識別不到返回的中文編碼格式,就會默認使用GB2312。如果返回時UTF-8編碼格式,由於編碼格式的不同,以至於瀏覽器頁面就會顯示亂碼。

2.5.2 解決響應後瀏覽器頁面顯示亂碼問題

我們在響應給瀏覽器信息的時候,要設置瀏覽器接收響應的編碼格式。如下:

response.setContentType("text/html;charset=utf-8");//內容類型;編碼格式

三、Servlet與JDBC集成的綜合案例

此案例引入了mysql、Druild連接池工具和apache的dbUtils工具等相關jar包,聯合完成的案例小項目

目錄結構如下:

在這裏插入圖片描述

db.properties配置文件
#連接設置
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8
username=root
password=Mylifes1110
#初始化連接
initialSize=10
#最大連接數量
maxActive=30
#最小空閒連接
minIdle=5
#超時等待時間以毫秒爲單位   1000毫秒等於1秒
maxWait=5000
數據庫
create table user
(
    id       int auto_increment
        primary key,
    username char(20) not null,
    password char(20) not null,
    phone    char(11) null
);
entity實體類
package com.mylifes1110.java.entity;

/**
 * @author Ziph
 */
public class User {
    private int id;
    private String username;
    private String password;
    private String phone;

    public User() {
    }

    public User(int id, String username, String password, String phone) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.phone = phone;
    }

    public User(String username, String password, String phone) {
        this.username = username;
        this.password = password;
        this.phone = phone;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}
utils工具類
package com.mylifes1110.java.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author Ziph
 */
public class DruidUtils {
    private static DruidDataSource dataSource;
    static {
        Properties properties = new Properties();
        InputStream resourceAsStream = DruidUtils.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(resourceAsStream);
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static DruidDataSource getDataSource() {
        return dataSource;
    }
}
UserDao接口
package com.mylifes1110.java.dao;

import com.mylifes1110.java.entity.User;

import java.util.List;

/**
 * @author Ziph
 */
public interface UserDao {
    /**
     * 查詢所有用戶信息
     *
     * @return 返回用戶信息的List集合
     */
    List<User> getAllUser();

    /**
     * 添加用戶信息
     *
     * @param user User
     * @return number int
     */
    int insert(User user);
}
UserDaoImpl實體類
package com.mylifes1110.java.dao.impl;

import com.mylifes1110.java.dao.UserDao;
import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * @author Ziph
 */
public class UserDaoImpl implements UserDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    @Override
    public List<User> getAllUser() {
        try {
            List<User> userList = queryRunner.query("select id, username, password, phone from user", new BeanListHandler<User>(User.class));
            return userList;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int insert(User user) {
        try {
            int insert = queryRunner.update("insert into user (username, password, phone) values (?, ?, ?)", user.getUsername(), user.getPassword(), user.getPhone());
            return insert;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
}
UserService接口
package com.mylifes1110.java.service;

import com.mylifes1110.java.entity.User;

import java.util.List;

/**
 * @author Ziph
 */
public interface UserService {
    /**
     * 實現查詢所用用戶信息功能
     *
     * @return 返回用戶信息的List集合
     */
    List<User> inquireAll();

    /**
     * 實現添加用戶信息功能
     *
     * @param user User
     * @return 返回受影響行數
     */
    int add(User user);
}
UserServiceImpl實體類
package com.mylifes1110.java.service.impl;

import com.mylifes1110.java.dao.UserDao;
import com.mylifes1110.java.dao.impl.UserDaoImpl;
import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.service.UserService;

import java.util.List;

/**
 * @author Ziph
 */
public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    @Override
    public List<User> inquireAll() {
        return userDao.getAllUser();
    }

    @Override
    public int add(User user) {
        return userDao.insert(user);
    }
}
GetAllUserServlet
package com.mylifes1110.java.servlet;

import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.service.UserService;
import com.mylifes1110.java.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * @author Ziph
 */
@WebServlet(name = "GetAllUserServlet", value = "/getAll")
public class GetAllUserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.調用Service
        response.setContentType("text/html;charset=utf-8");
        UserService userService = new UserServiceImpl();
        List<User> userList = userService.inquireAll();
        PrintWriter printWriter = response.getWriter();
        //頁面
        printWriter.println("<html>");
        printWriter.println("<head>");
        printWriter.println("<meta charset='utf-8' />");
        printWriter.println("<title>查詢所有</title>");
        printWriter.println("<body>");
        printWriter.println("<table border='1'>");
        printWriter.println("<tr>");
        printWriter.println("<th>編號</th>");
        printWriter.println("<th>用戶名</th>");
        printWriter.println("<th>密碼</th>");
        printWriter.println("<th>手機號</th>");
        printWriter.println("</tr>");
        for (User user : userList) {
            printWriter.println("<tr>");
            printWriter.println("<td>");
            printWriter.println(user.getId());
            printWriter.println("</td>");
            printWriter.println("<td>");
            printWriter.println(user.getUsername());
            printWriter.println("</td>");
            printWriter.println("<td>");
            printWriter.println(user.getPassword());
            printWriter.println("</td>");
            printWriter.println("<td>");
            printWriter.println(user.getPhone());
            printWriter.println("</td>");
            printWriter.println("</tr>");
        }
        printWriter.println("</table>");
        printWriter.println("</body>");
        printWriter.println("</head>");
        printWriter.println("</html>");
    }
}
InsertUserServlet
package com.mylifes1110.java.servlet;

import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.service.UserService;
import com.mylifes1110.java.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author Ziph
 */
@WebServlet(name = "InsertUserServlet", value = "/insert")
public class InsertUserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String phone = request.getParameter("phone");
        PrintWriter printWriter = response.getWriter();
        if (username.equals("") || username.trim().length() == 0) {
            printWriter.println("用戶名不能爲空!");
            return;
        }
        if (password.equals("") || password.trim().length() == 0) {
            printWriter.println("密碼不能爲空!");
            return;
        }
        User user = new User(username, password, phone);
        UserService userService = new UserServiceImpl();
        int result = userService.add(user);
        if (result > 0) {
            printWriter.println("新增成功!");
        } else {
            printWriter.println("新增失敗!");
        }
    }
}

index.html頁面(添加用戶頁面)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加學生</title>
</head>
<body>
    <form method="post" action="/sjdemo/insert">
        <table>
            <tr>
                <td>用戶名</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密碼</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td>手機號</td>
                <td><input type="text" name="phone"></td>
            </tr>
            <tr>
                <td><input type="submit" value="提交"></td>
                <td><input type="reset" value="重置"></td>
            </tr>
        </table>
    </form>
</body>
</html>

四、頁面跳轉(重定向)

作爲後臺開發人員,我們大多時候都在接收處理用戶請求,給予用戶響應,爲了方便操作,服務器軟件將請求和響應封裝成了request和response,此章節展示Java Web服務端控制頁面跳轉主要有兩種:重定向和轉發

4.1 重定向

重定向就是通過各種方法將網絡請求重新定個方向轉到其它位置。

4.1.1 重定向實現原理

客戶瀏覽器發送http請求 - > web服務器接受後發送302狀態碼響應及對應新的location給客戶瀏覽器 - > 客戶瀏覽器發現是302響應,則自動再發送一個新的http請求,請求url是新的location地址 - > 服務器根據此請求尋找資源併發送給客戶。在這裏location可以重定向到任意URL,既然是瀏覽器重新發出了請求,則就沒有什麼request傳遞的概念了。在客戶瀏覽器路徑欄顯示的是其重定向的路徑,客戶可以觀察到地址的變化的。

在這裏插入圖片描述

4.1.2 重定向的特點
  1. 重定向是客戶端行爲。
  2. 重定向是瀏覽器做了至少兩次的訪問請求。
  3. 重定向瀏覽器地址改變。
  4. 重定向兩次跳轉之間傳輸的信息會丟失(request範圍)。
  5. 重定向可以指向任何的資源,包括當前應用程序中的其他資源,同一個站點上的其他應用程序中的資源,其他站點的資源。

注意:傳遞給HttpServletResponse.sendRedirect 方法的相對URL以“/”開頭,它是相對於整個WEB站點的根目錄

4.1.3 利用重定向實現頁面跳轉

在我們jdbc和Servlet集成綜合案例中的添加用戶功能,響應瀏覽器“添加成功”和“添加失敗”改爲重定向爲提示頁面,來實現反饋給用戶提示信息!

重定向核心代碼:response.sendRedirect("/項目名稱/XXX.html");

項目中代碼:response.sendRedirect("/sjdemo/insertok.html");

<!-- 頁面1 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>帥哥美女,你新增成功了!</h1>
</body>
</html>

<!-- 頁面2 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>對不起,您新增失敗了,很遺憾!</h1>
</body>
</html>
package com.mylifes1110.java.servlet;

import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.service.UserService;
import com.mylifes1110.java.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author Ziph
 */
@WebServlet(name = "InsertUserServlet", value = "/insert")
public class InsertUserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String phone = request.getParameter("phone");
        PrintWriter printWriter = response.getWriter();
        if (username.equals("") || username.trim().length() == 0) {
            printWriter.println("用戶名不能爲空!");
            return;
        }
        if (password.equals("") || password.trim().length() == 0) {
            printWriter.println("密碼不能爲空!");
            return;
        }
        User user = new User(username, password, phone);
        UserService userService = new UserServiceImpl();
        int result = userService.add(user);
        if (result > 0) {
            /**
             * 重定向
             */
            response.sendRedirect("/sjdemo/insertok.html");
        } else {
            response.sendRedirect("/sjdemo/inserterror.html");
        }
    }
}
4.1.4 重定向的第二種方式

接收請求後,響應時攜帶302重定向狀態碼和響應頭location(新地址)來完成重定向

		if (result > 0) {
            /**
             * 重定向
             */
			//1.操作響應狀態碼
            response.setStatus(302);
			//2.操作響應頭location
			response.setHeader("location", "/sjdemo/insertok.html");
        } else {
            //1.操作響應狀態碼
            response.setStatus(302);
			//2.操作響應頭location
			response.setHeader("location", "/sjdemo/inserterror.html");
        }

4.2 請求轉發

Servlet除了支持重定向之外還支持請求轉發

4.2.1 請求轉發實現原理

客戶瀏覽器發送http請求 - > web服務器接受此請求 - > 調用內部的一個方法在容器內部完成請求處理和轉發動作 - > 將目標資源發送給客戶。在這裏,轉發的路徑必須是同一個web容器下的url,其不能轉向到其他的web路徑上去,中間傳遞的是自己的容器內的request。在客戶瀏覽器路徑欄顯示的仍然是其第一次訪問的路徑,也就是說客戶是感覺不到服務器做了轉發的。

注意:內部轉發檢驗是否登錄京東賬號。是,發出響應顯示收藏夾頁面。否,發出相應顯示登錄頁面

在這裏插入圖片描述

4.2.2 請求轉發的特點
  1. 轉發是服務器行爲
  2. 轉發是瀏覽器只做了一次訪問請求
  3. 轉發瀏覽器地址不變
  4. 轉發兩次跳轉之間傳輸的信息不會丟失,所以可以通過request進行數據的傳遞
  5. 轉發只能將請求轉發給同一個WEB應用中的組件

注意:如果創建RequestDispatcher 對象時指定的相對URL以“/”開頭,它是相對於當前WEB應用程序的根目錄。

4.2.3 利用請求轉發實現功能

再次利用我們Servlet與JDBC集成的綜合案例,當添加用戶後直接請求轉發到查詢所有用戶的界面!

請求轉發核心代碼:request.getRequestDispatcher("/項目資源名稱").forward(request, response);

項目中代碼:request.getRequestDispatcher("/getAll").forward(request, response);

注意:請求轉發必須是在同一項目資源下,所以只用了 / ,不用/後追加項目名稱(默認必須是同一項目)

package com.mylifes1110.java.servlet;

import com.mylifes1110.java.entity.User;
import com.mylifes1110.java.service.UserService;
import com.mylifes1110.java.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author Ziph
 */
@WebServlet(name = "InsertUserServlet", value = "/insert")
public class InsertUserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String phone = request.getParameter("phone");
        PrintWriter printWriter = response.getWriter();
        if (username.equals("") || username.trim().length() == 0) {
            printWriter.println("用戶名不能爲空!");
            return;
        }
        if (password.equals("") || password.trim().length() == 0) {
            printWriter.println("密碼不能爲空!");
            return;
        }
        User user = new User(username, password, phone);
        UserService userService = new UserServiceImpl();
        int result = userService.add(user);
        if (result > 0) {
            /**
             * 利用轉發添加成功後直接查詢所有用戶信息
             * 注意:轉發必須是同一項目資源下,所以路徑寫 /servlet資源名稱 (/getAll)
             */
            request.getRequestDispatcher("/getAll").forward(request, response);
        } else {
            /**
             * 重定向
             */
            response.sendRedirect("/sjdemo/inserterror.html");
        }
    }
}

4.3 response對象操作響應行和響應頭

4.3.1 操作響應行
4.3.1.1 操作正常的響應狀態碼

利用Response對象操作正常響應狀態碼

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            /**
             * response對象操作響應行(響應狀態碼)
             * 操作正常狀態碼 : 200 , 302等
             * 200:告訴瀏覽器,響應成功
             * 302:重定向
             * 304:頁面沒有發生
             */
            response.setStatus(200);
        }
4.3.1.2 操作錯誤的響應狀態碼

利用Response對象操作錯誤的響應狀態碼

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            /**
             * 操作錯誤狀態碼
             */
            response.sendError(404);
        }
4.3.2 操作響應頭
4.3.2.1 添加新值
	public void addHeader(String name, String value):操作響應頭,在原有基礎上添加新值 

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            /**
             * 操作響應頭
             * setHeader():覆蓋原有的響應頭的值
             * addHeader():在原有的響應頭的值後面追加 (Cookie)
             * 服務器告訴瀏覽器,給的響應正文的類型是text/html,告訴你應該以utf-8進行解碼
             */
            response.setHeader("Content-Type", "text/html;charset=utf-8");
        }
4.3.2.2 直接賦值

public void setHeader(String name, String value):操作響應頭,直接賦值

下一篇狀態管理會講到關於Cookei的內容!

4.4 頁面的定時跳轉

操作響應頭refresh,完成定時跳轉

			/**
    		* 定時跳轉
    		* 添加學生成功後,顯示空白頁5秒後跳轉到inserok.html頁面提示添加學生成功!
    		*/
    		response.setHeader("refresh", "5;url=/sjdemo/insertok.html");

4.5 Request對象操作請求行、請求頭和請求參數

如下所有代碼都遵循該註解配置: @WebServlet(name = “RequestServletDemo”, value = “/test1”)

4.5.1 操作請求行
4.5.1.1 獲取請求路徑
    		/**
             * 獲取請求路徑:
             * 請求路徑爲:http://localhost:8080/reqweb/test1
             */
            StringBuffer requestURL = request.getRequestURL();
            System.out.println("請求路徑爲:" + requestURL);

4.5.1.2 獲取請求方式
    		/**
             * 獲取請求方式:
             * 請求方式爲:GET
             */
            String requestMethod = request.getMethod();
            System.out.println("請求方式爲:" + requestMethod);

4.5.1.3 獲取請求地址上面的IP地址
    		/**
             * 獲取請求地址上面的IP地址:(此地址是翻譯的localhost)
             * 請求IP地址:0:0:0:0:0:0:0:1
             */
            String requestRemoteAddr = request.getRemoteAddr();
            System.out.println("請求IP地址:" + requestRemoteAddr);
4.5.1.4 獲取請求地址上面的端口號
    		/**
             * 獲取請求地址上面的端口號:
             * 請求端口號爲:8080
             */
            int requestLocalPort = request.getLocalPort();
            System.out.println("請求端口號爲:" + requestLocalPort);
4.5.1.5 獲取請求地址後面拼接的內容(GET請求)
    		/**
             * 獲取請求地址後面拼接的內容:(GET請求有拼接內容,POST請求沒有)
             * 關於地址欄GET請求如何拼接後提交:http://localhost:8080/reqweb/test1?username=ziph&password=123456
             * 請求網址後的拼接內容爲:username=ziph&password=123456
             */
            String requestQueryString = request.getQueryString();
            System.out.println("請求網址後的拼接內容爲:" + requestQueryString);
4.5.2 操作請求頭
4.5.2.1 獲取User-Agent(瀏覽器類型)
    		/**
             * 獲取User-Agent請求頭:(判斷請求是由哪個瀏覽器發起的)
             * User-Agent爲:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36
             */
            String requestHeader = request.getHeader("User-Agent");
            System.out.println("User-Agent爲:" + requestHeader);

4.5.3 操作請求參數【重點】
4.5.3.1 獲取請求參數
    		/**
             * 獲取請求參數:
             * 瀏覽器地址欄內容爲:http://localhost:8080/reqweb/test3?username=ziph&password=123456
             * username : ziph	password : 123456
             */
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            System.out.println("username : " + username + "\t" + "password : " + password);


4.5.3.2 獲取所有請求參數名稱
    		/**
             * 獲取所有請求參數名稱
             * 瀏覽器地址欄內容爲:http://localhost:8080/reqweb/test3?username=ziph&password=123456
             * name : username	value : ziph
             * name : password	value : 123456
             * 注意:遍歷所有枚舉name並獲取其對應value,打印!
             */
            Enumeration<String> requestParameterNames = request.getParameterNames();
            while (requestParameterNames.hasMoreElements()) {
                String parameterName = requestParameterNames.nextElement();
                String parameterValue = request.getParameter(parameterName);
                System.out.println("name : " + parameterName + "\t" + "value : " + parameterValue);
            }

4.5.3.3 獲取指定參數的所有值
    		/**
             * 獲取指定參數的所有值
             * 瀏覽器地址欄內容爲:http://localhost:8080/reqweb/test3?username=ziph&password=123456
             * ziph
             * </p>
             * 傳入多個username並打印多個username值
             * 瀏覽器地址欄內容爲:http://localhost:8080/reqweb/test3?username=ziph&username=join&username=marry
             * ziph
             * join
             * marry
             */
            String[] usernames = request.getParameterValues("username");
            System.out.println(usernames[0]);
            String[] usernames1 = request.getParameterValues("username");
            for (String name : usernames1) {
                System.out.println(name);
            }
4.5.3.4 獲取請求參數對應的Map集合
    		/**
             * 獲取請求參數對應的Map集合
             * 鍵:請求參數名稱  相當於 getParameterNames
             * 值:一組請求參數值 相當於 getParameterValues
             * 參數名稱 : username參數值 : ziph join marry
             * 參數名稱 : password參數值 : 123456 123456 123456
             */
            Map<String, String[]> requestParameterMap = request.getParameterMap();
            Set<Map.Entry<String, String[]>> entrySet = requestParameterMap.entrySet();
            for (Map.Entry<String, String[]> entry : entrySet) {
                //鍵 - 請求參數名稱
                String parameterName = entry.getKey();
                //值 - 一組請求參數值
                String[] values = entry.getValue();
                StringBuffer buffer = new StringBuffer();
                for (String value : values) {
                    buffer.append(value + " ");
                }
                System.out.println("參數名稱 : " + parameterName + "參數值 : " + buffer);
            }

五、Servlet的生命週期

5.1 什麼是生命週期?

生命週期就是指一個對象的生老病死。 拿產品來做例子說明,就是包括製造產品所需要的原材料的採集、加工等生產過程,也包括產品貯存、運輸等流通過程,還包括產品的使用過程以及產品報廢或處置等廢棄回到自然過程,這個過程構成了一個完整的產品的生命週期。

5.2 Servlet生命週期的四個階段

先說一下從調用自身構造器開始Servlet的六個階段: 實例化(調用構造器)初始化獲取配置信息提供服務返回有關信息銷燬

其實它們都是整個生命週期中的一員,但是我們選取重要的生命週期成員就有四個階段:實例化(調用構造器)初始化提供服務銷燬

階段一、實例化(調用構造方法)
實例化階段是Servlet生命週期中的第一步,由Servlet容器調用Servlet的構造器創建一個具體的Servlet對象的過程。而這個創建的時機可以是在容器收到針對這個組件的請求之後,即用了才創建;也可以在容器啓動之後立刻創建實例,而不管此時Servlet是否使用的上。使用如下代碼可以設置Servlet是否在服務器啓動時就執行創建
<load-on-startup>1</load-on-startup> //影響創建的時機

階段二、初始化(init方法)
Servlet在被加載實例化之後,必須要初始化它。在初始化階段,init()方法會被調用。這個方法在javax.servlet.Servlet接口中定義。其中,方法以一個ServletConfig類型的對象作爲參數。ServletConfig對象由Servlet引擎負責創建,從中可以讀取到事先在web.xml文件中通過<init-param>節點配置的多個name-value名值對。ServletConfig對象還可以讓Servlet接受一個ServletContext對象。
一般情況下,init方法不需要編寫,因GenericServlet已經提供了init方法的實現,並且提供了getServletConfig方法來獲得ServletConfig對象。
注意:init方法只被執行一次

階段三、提供服務/就緒(service方法)
Servlet被初始化以後就處於能夠響應請求的就緒狀態。每個對Servlet的請求由一個ServletRequest對象代表,Servlet給客戶端的響應由一個ServletResponse對象代表。當客戶端有一個請求時,容器就會將請求與響應對象轉給Servlet,以參數的形式傳給service方法。service方法由javax.servlet.Servlet定義,由具體的Servlet實現,而HttpServlet將service方法拆分了成我們常用的doGet和doPost方法

階段四、銷燬(destroy方法)
Servlet容器在銷燬Servlet對象時會調用destroy方法來釋放資源。通常情況下Servlet容器停止或者重新啓動都會引起銷燬Servlet對象的動作,但除此之外,Servlet容器也有自身管理Servlet對象的準則,整個生命週期並不需要人爲進行干預

5.2.1 HttpServlet生命週期演示
package com.mylifes1110.java.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Ziph
 */
@WebServlet(name = "HttpServlet", value = "/test")
public class HttpServlet extends javax.servlet.http.HttpServlet {
    /**
     * 默認無參構造器
     */
    public HttpServlet() {
        System.out.println("1.調用無參構造器");
    }

    /**
     * 調用init方法進行初始化
     */
    @Override
    public void init() throws ServletException {
        super.init();
        System.out.println("2.進行初始化工作");
    }

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

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        System.out.println("3.提供服務中...");
        response.getWriter().append("Servlet at:").append(request.getContextPath());
    }

    /**
     * 調用destroy方法進行銷燬
     */
    @Override
    public void destroy() {
        super.destroy();
        System.out.println("4.進行銷燬");
    }
}

在這裏插入圖片描述

六、Sevlet線程安全問題

6.1 線程安全問題

一個Web容器中只創建一個Servlet對象(單例設計模式),因爲每次請求都會創建一個線程,如果多人併發請求,那麼就會存在多個線程操作同一個Servlet對象,那麼如果在對應的方法中操作了成員變量,就會可能產生線程安全問題!

6.2 如何保證線程安全問題

如何保證線程安全問題有三種方法:加synchronized同步代碼塊(鎖)實現SingleThreadModle接口使用局部變量

package com.mylifes1110.java.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "SafeServlet", value = "/safe")
public class SafeServlet extends HttpServlet {
    int num = 0;
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	num++;
        System.out.println("當前訪問次數爲:" + num);
    }
}

問題: 會出現多個線程發出請求,訪問的是同一個Servlet對象,也就是說num值訪問的是同一個,它會一直做自增1操作

注意: 多個線程發出請求正常應該是訪問多個Servlet對象,也就是一個線程對應一個num值來自增1記錄該線程的訪問次數

6.2.1 加synchronized同步代碼塊(鎖)

加synchronized同步代碼塊(鎖)來保證線程安全


6.2.2 實現SingleThreadModle接口

實現SingleThreadModle接口來保證線程安全

在這裏插入圖片描述

在這裏插入圖片描述

6.2.3 使用局部變量

在Servlet中儘量使用局部變量來保證線程安全

注意: 雖然是用的同一個Servlet對象,但是方法中的局部變量不是公用的,而是每個線程訪問該方法的局部變量獨一份

package com.mylifes1110.java.testservlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "SafeServlet2", value = "/safe2")
public class SafeServlet2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * 測試方法中的線程
         */
        System.out.println(Thread.currentThread().getId() + "已啓動!");
        
        int num = 0;
        num++;
        System.out.println("被訪問的次數爲:" + num);
    }
}

七、Servlet初始化參數及配置

7.1 什麼是Servlet初始化參數

Servlet初始化參數是Servlet調用init初始化方法同時加載的參數

7.2 瞭解Servlet初始化參數機制

既然是瞭解初始化機制,我們就必須去翻看源碼了!翻看源碼時,我們發現init()初始化方法是寫在GenericServlet類中的,而且有兩個一個是帶參數的,一個是不帶參數的。

private transient ServletConfig config;//源碼中聲明的ServletConfig對象是空的

在這裏插入圖片描述

看到重載的方法中傳入了一個ServletConfig對象,然後內部處理初始化了該參數this.config = config,既然初始化了該參數,我們就可以使用getServletConfig方法去調用獲得該初始化配置對象了!

我們有沒有一個疑問呢?是誰傳給init方法中的ServletConfig對象呢?仔細想一下,我們可以想到是tomact容器調用init初始化方法傳入的ServletConfig對象,所以如果我們想要用get方法去獲取該初始化配置參數,就需要我們去配置初始化參數了!那就繼續看看是如何配置初始化參數的吧!

7.3 Servlet初始化參數配置方式

Servlet初始化參數配置方式有兩種:xml配置文件配置註解配置

7.3.1 xml配置文件配置初始化參數

配置參數如下:

init-param元素用來定義Servlet啓動的參數,可以定義多個

param-name表示參數名稱

param-value表示參數值

獻上xml配置文件配置信息,如下:

<servlet>
        <servlet-name>init1</servlet-name>
        <servlet-class>com.mylifes1110.java.testservlet.InitParamServlet1</servlet-class>
    
		<!-- 可以配置多個init-param(初始化參數) -->
        <init-param>
            <param-name>username</param-name>
            <param-value>Ziph</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123456</param-value>
        </init-param>
    
    </servlet>
    <servlet-mapping>
        <servlet-name>init1</servlet-name>
        <url-pattern>/init1</url-pattern>
    </servlet-mapping>
</web-app>

Java代碼如下:

package com.mylifes1110.java.testservlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class InitParamServlet1 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * 初始化參數獲取當前相關所有配置信息
         * 注意:因爲是繼承GenericServlet類得到的getServletConfig方法,可以直接this獲取
         */
        ServletConfig servletConfig = this.getServletConfig();
        String username = servletConfig.getInitParameter("username");
        String password = servletConfig.getInitParameter("password");
        /**
         * 控制檯打印結果:username : Ziph	password : 123456
         */
        System.out.println("username : " + username + "\t" + "password : " + password);
    }

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

7.3.2 使用註解配置初始化參數

配置參數如下:

initParams:配置Servlet的初始化參數

注意: 在註解中的initParams默認是一個空數組,而數組中傳入的參數即爲:@WebInitParam(),再看@WebInitParam()中需要傳入的參數即爲:name和value(需要傳入字符串類型)

註解寫法參考如下:

@WebServlet(name = "InitParamServlet", value = "/init", initParams = {@WebInitParam(name = "username", value = "Ziph"), @WebInitParam(name = "password", value = "123456")})

完整Java代碼如下:

package com.mylifes1110.java.testservlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "InitParamServlet", value = "/init", initParams = {@WebInitParam(name = "username", value = "Ziph"), @WebInitParam(name = "password", value = "123456")})
public class InitParamServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletConfig servletConfig = this.getServletConfig();
        String username = servletConfig.getInitParameter("username");
        String password = servletConfig.getInitParameter("password");
        /**
         * 打印結果爲:username : Ziph	password : 123456
         */
        System.out.println("username : " + username + "\t" + "password : " + password);
    }
}

Servlet內容較多,所以把狀態管理內容放在了下一篇【JavaWeb之Servlet狀態管理】,下一章內容概括Cookei、Session和ServletContext對象

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