1. 程式人生 > 實用技巧 >Web中Servlet簡單總結

Web中Servlet簡單總結

Web階段

HTTP協議概述

主要可以理解為:客戶端-發起請求=====伺服器-響應請求

  • HTTP(Hyper Text Transfer Protocol):超文字傳輸協議

  • HTTP 協議是基於 TCP/IP 協議的

  • 超文字:比普通文字更加強大

  • 傳輸協議:客戶端和伺服器端的通訊規則(握手規則)

請求部分

請求行 請求頭 請求空行 請求體

請求的方式:get/post

注意:只有 POST 請求方式才有請求體

常用狀態碼介紹:

狀態碼說明
200 一切都OK>
302/307 請求重定向(客戶端行為,兩次請求,位址列發生改變)
304 請求資源未發生變化,使用快取
404 請求資源未找到
500 伺服器錯誤

Servlet(重中之重)

servlet介紹:

Servlet 是執行在 Java 伺服器端的程式,用於接收和響應來自客戶端基於 HTTP 協議的請求

  • 第一:Servlet是一個執行在web服務端的java小程式

  • 第二:它可以用於接收和響應客戶端的請求

  • 第三:要想實現Servlet功能,可以實現Servlet介面,繼承GenericServlet或者HttpServlet

  • 第四:每次請求都會執行service方法

  • 第五:Servlet還支援配置

servlet的執行流程

Servlet關係檢視

Servlet實現方式

共有3種方式(一般只用第三種)

  1. 第一種 實現 Servlet 介面,實現所有的抽象方法。該方式支援最大程度的自定義。

  2. 第二種 繼承 GenericServlet 抽象類,必須重寫 service 方法,其他方法可選擇重寫。該方式讓我們開發 Servlet 變得簡單。但是這種方式和 HTTP 協議無關。

  3. 第三種 繼承 HttpServlet 抽象類,需要重寫 doGet 和 doPost 方法。該方式表示請求和響應都需要和 HTTP 協議相關。

Servlet的生命週期

總結來說,只要有請求就是Servlet的誕生之日,請求結束伺服器停止,Servlet死亡

Servlet的執行緒安全

和執行緒安全一樣,需要加鎖,注意上鎖的物件要唯一

package com.itheima.servlet;

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

/*
Servlet執行緒安全
*/
public class ServletDemo04 extends HttpServlet{
//1.定義使用者名稱成員變數
//private String username = null;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String username = null;
synchronized (this) { //鎖需要唯一,Servlet物件就是唯一的,所以用this
//2.獲取使用者名稱
username = req.getParameter("username");

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//3.獲取輸出流物件
PrintWriter pw = resp.getWriter();

//4.響應給客戶端瀏覽器
pw.print("welcome:" + username);

//5.關流
pw.close();
}
}

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

Servlet的對映方式

三種:(經常使用第一種)

  1. 第一種 具體名稱的方式。訪問的資源路徑必須和對映配置完全相同

  2. 第二種 / 開頭 + 萬用字元的方式。只要符合目錄結構即可,不用考慮結尾是什麼

  3. 第三種 萬用字元 + 固定格式結尾的方式。只要符合固定結尾格式即可,不用考慮前面的路徑

注意:優先順序問題。越是具體的優先順序越高,越是模糊通用的優先順序越低。第一種 -> 第二種 -> 第三種

Servlet多對映的使用場景

場景分析:

  • 如果訪問的資源路徑是 /vip 商品價格打9折

  • 如果訪問的資源路徑是 /vvip 商品價格打5折

  • 如果訪問的資源路徑是其他 商品價格不打折

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
Servlet 多路徑對映
*/
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 定義一個商品金額
int money = 1000;

//2. 獲取訪問的資源路徑
String name = req.getRequestURI();
name = name.substring(name.lastIndexOf("/"));

//3. 條件判斷
if("/vip".equals(name)) {
//如果訪問資源路徑是/vip 商品價格為9折
System.out.println("商品原價為:" + money + "。優惠後是:" + (money*0.9));
} else if("/vvip".equals(name)) {
//如果訪問資源路徑是/vvip 商品價格為5折
System.out.println("商品原價為:" + money + "。優惠後是:" + (money*0.5));
} else {
//如果訪問資源路徑是其他 商品價格原樣顯示
System.out.println("商品價格為:" + money);
}
}

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

ServletConfig的使用

  • <servlet>標籤中,通過<init-param>標籤來配置。有兩個子標籤。

  • <param-name>:代表初始化引數的 key。

  • <param-value>:代表初始化引數的 value。

常用方法

ServletContext

作用:可以獲得應用的全域性初始化引數和達到 Servlet 之間的資料共享。

ServletContext圖示:

域物件概念

  • 域物件指的是物件有作用域也就是有作用範圍

  • 域物件可以實現資料的共享

  • 不同作用範圍的域物件,共享資料的能力也不一樣

  • 在 Servlet 規範中,一共有 4 個域物件

    ServletContext 就是其中的一個

ServletContext的使用

ServletContext 並不屬於某個 Servlet 的配置,而是針對於整個應用的配置,也叫全域性的初始化引數

<web-app>標籤中,通過<context-param>標籤來配置。有兩個子標籤

<param-name>:代表全域性初始化引數的 key

<param-value>:代表全域性初始化引數的 value

配置Servlet,並且配置ServletContext

<web-app>
....
<!--配置Servlet-->
<servlet>
<servlet-name>servletContextDemo</servlet-name>
<servlet-class>com.itheima.servlet.ServletContextDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletContextDemo</servlet-name>
<url-pattern>/servletContextDemo</url-pattern>
</servlet-mapping>


<!--配置ServletContext-->
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>globalDesc</param-name>
<param-value>This is ServletContext</param-value>
</context-param>
</web-app>

常用方法

 //獲取ServletContext物件
ServletContext context = getServletContext();
//修改ServletContextDemo:儲存資料
//向域物件中儲存資料
context.setAttribute("username","zhangsan");
//修改ServletConfigDemo:獲取資料
//獲取ServletContextDemo設定共享的資料
Object username = context.getAttribute("username");
System.out.println(username);

自動註解開發Servlet

不需要web下的WEB-INF, web.xml也沒有了

index.jsp也沒用

Servlet應用案例-學生管理系統 ***

案例效果介紹

  • 效果

    • 訪問案例首頁,看到一個可以儲存學生資訊的介面

    • 輸入內容,點選儲存,通過java伺服器,然後最終儲存到txt中

    • 最後java伺服器返回成功結果

案例實現

  1. 建立一個 web 專案:servlet_test,配置虛擬目錄/stu

    • 選擇javaee 7,這裡我們還是用web.xml

  2. 建立一個用於儲存學生資訊的 html 檔案:web下新建addStudent.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>新增學生</title>
    </head>
    <body>
    -- ?username=張三&age=18&score=718
    <form action="/stu/studentServlet" method="get" autocomplete="off">
    學生姓名:<input type="text" name="username"> <br/>
    學生年齡:<input type="number" name="age"> <br/>
    學生成績:<input type="number" name="score"> <br/>
    <button type="submit">儲存</button>
    </form>
    </body>
    </html>
  3. 建立一個類com.itheima.servlet.StudentServlet,繼承 HttpServlet

  4. 重寫 doGet 和 doPost 方法

    package com.itheima.servlet;

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

    public class StudentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req,resp);
    }
    }
  5. 在 web.xml 檔案中修改預設主頁和配置 Servlet

     <!--修改預設主頁-->
    <welcome-file-list>
    <welcome-file>/addStudent.html</welcome-file>
    </welcome-file-list>

    <!--配置Servlet-->
    <servlet>
    <servlet-name>studentServlet</servlet-name>
    <servlet-class>com.itheima.servlet.StudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/studentServlet</url-pattern>
    </servlet-mapping>
  6. 在 doGet 方法中接收表單資料儲存到檔案中,並響應給瀏覽器結果

    // -- ?username=張三&age=18&score=718
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //獲取表單中的資料
    String username = req.getParameter("username"); // 獲取url後邊的?的引數
    String age = req.getParameter("age");
    String score = req.getParameter("score");

    //將資料儲存到stu.txt檔案中
    BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\stu.txt",true));
    bw.write(username + "," + age + "," + score);
    bw.newLine();
    bw.close();

    //給瀏覽器迴應
    PrintWriter pw = resp.getWriter();
    pw.println("Save Success~");
    pw.close();
    }
  7. 部署並啟動專案

  8. 通過瀏覽器測試

請求物件

請求物件常用方法1-獲取各種路徑

request.

獲取請求引數工具類封裝,可以使用apache的beanutils包

用流的形式讀取請求資訊

注意:

獲取文字 字元時用getReader(字元流,如果採用字元流必須使用post請求方式)

獲取圖片 檔案時用getInputStream(位元組流)

請求正文中文編碼問題

post方式請求

//設定編碼格式
req.setCharacterEncoding("UTF-8");

get請求方式

//輸出到瀏覽器:注意響應的亂碼問題已經解決了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);

請求域

可以在一次請求範圍內共享資料,一般用於請求,轉發的多個資源中 共享資料

程式碼展示

響應物件

  • 響應:回饋結果。在 BS 架構中,就是伺服器給客戶端瀏覽器反饋結果

  • 響應物件:就是在專案中用於傳送響應的物件

//解決中文亂碼
resp.setContentType("text/html;charset=UTF-8");

案例:響應圖片

  1. 建立位元組輸入流物件,關聯讀取的圖片路徑

  2. 通過響應物件獲取位元組輸出流物件

  3. 迴圈讀取和寫出圖片

  4. 案例:新建ServletDemo03

/*
響應圖片到瀏覽器
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 建立位元組輸入流物件,關聯圖片路徑
String realPath = getServletContext().getRealPath("/img/hm.png");
System.out.println(realPath);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//注意:不能直接指定圖片路徑,需要同getRealPath獲取,因為專案還要釋出,我們需要獲取到釋出之後的圖片路徑
//BufferedInputStream bis = new BufferedInputStream(new FileInputStream(/img/hm.png));
//2. 獲取位元組輸出流物件
ServletOutputStream sos = resp.getOutputStream();

//3. 迴圈讀寫
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}

bis.close();
sos.close();
}

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

設定快取時間

String news = "這是一條很火爆的新聞~~";

//設定快取時間:1小時的快取時間
//引數1:Expires : 失效的意思
//引數2:當前時間+1小時毫秒值(意思就是在1小時之後過期)
resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L));

//設定編碼格式
resp.setContentType("text/html;charset=UTF-8");
//寫出資料
resp.getWriter().write(news);
System.out.println("aaa");
}

設定定時重新整理

 String news = "您的使用者名稱或密碼錯誤,3秒後自動跳轉到登入頁面...";

//設定編碼格式
resp.setContentType("text/html;charset=UTF-8");
//寫出資料
resp.getWriter().write(news);

//設定響應訊息頭定時重新整理
//Refresh:重新整理
//第二個引數第一部分:3,3設之後
//第二個引數第二部分:跳轉到哪個路徑
//resp.setHeader("Refresh","3;URL=/response/login.html");
resp.setHeader("Refresh","3;URL="+req.getContextPath()+"/login.html");
}

請求重定向

/*
請求重定向
*/
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設定請求域資料
req.setAttribute("username","zhangsan");

//設定重定向
resp.sendRedirect(req.getContextPath() + "/servletDemo07");
//resp.sendRedirect("/response/servletDemo07");
// resp.sendRedirect("https://www.baidu.com");
}
/*
請求重定向
*/
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo07執行了...");
Object username = req.getAttribute("username");//獲取不到
System.out.println(username);
}

總之一句話:重定向是兩次請求,設定的資料不會共享

  • 重定向與轉發的區別

  • 重定向:

    • 兩次請求

    • 位址列發生變化

    • 不可以使用request域共享資料 (既然是兩次請求,那肯定不能使用請求域中共享的資料)

    • 可以重定向到其他伺服器的url

  • 轉發:

    • 一次請求

    • 位址列不發生變化

    • 可以使用request域共享資料

    • 只能轉發到自己伺服器內部的url

響應物件-檔案下載

 //1.建立位元組輸入流,關聯讀取的檔案
        //獲取檔案的絕對路徑
        String realPath = getServletContext().getRealPath("/img/hm.png");
        //建立位元組輸出流物件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

        //2.設定響應頭支援的型別  應用支援的型別為位元組流
        /*
            Content-Type 訊息頭名稱   支援的型別
            application/octet-stream   訊息頭引數  應用型別為位元組流
         */
        resp.setHeader("Content-Type","application/octet-stream");

        //3.設定響應頭以下載方式開啟  以附件形式處理內容
        /*
            Content-Disposition  訊息頭名稱  處理的形式
            attachment;filename=  訊息頭引數  附件形式進行處理;指定下載檔名稱
         */
        resp.setHeader("Content-Disposition","attachment;filename=hm.png");

        //4.獲取位元組輸出流物件
        ServletOutputStream sos = resp.getOutputStream();

        //5.迴圈讀寫檔案
        byte[] arr = new byte[1024];
        int len;
        while((len = bis.read(arr)) != -1) {
            sos.write(arr,0,len);
        }

        //6.釋放資源
        bis.close();
        //sos.close();
    }