Web中Servlet簡單總結
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種方式(一般只用第三種)
-
第一種 實現 Servlet 介面,實現所有的抽象方法。該方式支援最大程度的自定義。
-
第二種 繼承 GenericServlet 抽象類,必須重寫 service 方法,其他方法可選擇重寫。該方式讓我們開發 Servlet 變得簡單。但是這種方式和 HTTP 協議無關。
-
第三種 繼承 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的對映方式
三種:(經常使用第一種)
-
第一種 具體名稱的方式。訪問的資源路徑必須和對映配置完全相同
-
第二種 / 開頭 + 萬用字元的方式。只要符合目錄結構即可,不用考慮結尾是什麼
-
第三種 萬用字元 + 固定格式結尾的方式。只要符合固定結尾格式即可,不用考慮前面的路徑
注意:優先順序問題。越是具體的優先順序越高,越是模糊通用的優先順序越低。第一種 -> 第二種 -> 第三種
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伺服器返回成功結果
-
案例實現
-
建立一個 web 專案:servlet_test,配置虛擬目錄/stu
-
選擇javaee 7,這裡我們還是用web.xml
-
-
建立一個用於儲存學生資訊的 html 檔案:web下新建addStudent.html
-
建立一個類com.itheima.servlet.StudentServlet,繼承 HttpServlet
-
重寫 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 {
-
在 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> -
在 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();
} -
部署並啟動專案
-
通過瀏覽器測試
請求物件
請求物件常用方法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");
案例:響應圖片
-
建立位元組輸入流物件,關聯讀取的圖片路徑
-
通過響應物件獲取位元組輸出流物件
-
迴圈讀取和寫出圖片
-
案例:新建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();
}