課程內容:
SpringBootWeb 入門
Http協議
SpringBootWeb案例
分層解耦
文章目錄
- 1. SpringBootWeb入門
- 1.1 概述
- 1.2 入門程序
- 1.2.1 需求
- 1.2.2 開發步驟
- 1.2.3 常見問題
- 1.3 入門解析
- 2. HTTP協議
- 2.1 HTTP概述
- 2.1.1 介紹
- 2.1.2 特點
- 2.2 HTTP請求協議
- 2.2.1 介紹
- 2.2.2 獲取請求數據
- 2.3 HTTP響應協議
- 2.3.1 格式介紹
- 2.3.2 響應狀態碼
- 2.3.3 設置響應數據
- 3. SpringBootWeb案例
- 3.1 需求說明
- 3.2 代碼實現
- 3.3 @ResponseBody
- 3.4 問題分析
- 4. 分層解耦
- 4.1 三層架構
- 4.1.1 介紹
- 4.1.2 代碼拆分
- 4.2 分層解耦
- 4.2.1 問題分析
- 4.2.2 解耦思路
- 4.3 IOC\&DI入門
- 4.4 IOC詳解
- 4.4.1 Bean的聲明
- 4.4.2 組件掃描
- 4.5 DI詳解
- 4.5.1 @Autowired用法
- 4.5.2 注意事項
- 附錄:常見狀態碼
那在前面講解Web前端開發的時候,我們學習了前端網頁開發的三劍客HTML、CSS、JS,通過這三項技術,我們就可以制作前端頁面了。 那最終,這些個頁面資料,我們就可以部署在服務器上,然后打開瀏覽器就可以直接訪問服務器上部署的前端頁面了。
而像HTML、CSS、JS 以及圖片、音頻、視頻等這些資源,我們都稱為靜態資源。 所謂靜態資源,就是指在服務器上存儲的不會改變的數據,通常不會根據用戶的請求而變化。
那與靜態資源對應的還有一類資源,就是動態資源。那所謂動態資源,就是指在服務器端上存儲的,會根據用戶請求和其他數據動態生成的,內容可能會在每次請求時都發生變化。比如:Servlet、JSP等(負責邏輯處理)。而Servlet、JSP這些技術現在早都被企業淘汰了,現在在企業項目開發中,都是直接基于Spring框架來構建動態資源。
而對于我們java程序開發的動態資源來說,我們通常會將這些動態資源部署在Tomcat,這樣的Web服務器中運行。 而瀏覽器與服務器在通信的時候,基本都是基于HTTP協議的。
那上述所描述的這種瀏覽器/服務器的架構模式呢,我們稱之為:BS架構。
-
BS架構:Browser/Server,瀏覽器/服務器架構模式。客戶端只需要瀏覽器,應用程序的邏輯和數據都存儲在服務端。
-
優點:維護方便
-
缺點:體驗一般
-
-
CS架構:Client/Server,客戶端/服務器架構模式。需要單獨開發維護客戶端。例如QQ、微信、CS2
-
優點:體驗不錯,不需要聯網加載
-
缺點:開發維護麻煩
-
那前面我們已經學習了靜態資源開發技術,包括:HTML、CSS、JS以及JS的高級框架Vue,異步交互技術Axios。 那那接下來呢,我們就要來學習動態資料開發技術,而動態資源開發技術中像早期的Servlet、JSP這些個技術早都被企業淘汰了,現在企業開發主流的就是基于Spring體系中的框架來開發這些動態資源。 所以,我們今天的課程內容內,分為以下四個部分:
-
SpringBootWeb入門
-
HTTP協議
-
SpringBootWeb案例
-
分層解耦
1. SpringBootWeb入門
那接下來呢,我們就要來講解現在企業開發的主流技術 SpringBoot,并基于SpringBoot進行Web程序的開發 。
1.1 概述
在沒有正式的學習SpringBoot之前,我們要先來了解下什么是Spring。
我們可以打開Spring的官網(https://spring.io),去看一下Spring的簡介:Spring makes Java simple。
Spring的官方提供很多開源的項目,我們可以點擊上面的projects,看到spring家族旗下的項目,按照流行程度排序為:
Spring發展到今天已經形成了一種開發生態圈,Spring提供了若干個子項目,每個項目用于完成特定的功能。而我們在項目開發時,一般會偏向于選擇這一套spring家族的技術,來解決對應領域的問題,那我們稱這一套技術為spring全家桶。
而Spring家族旗下這么多的技術,最基礎、最核心的是 SpringFramework。其他的spring家族的技術,都是基于SpringFramework的,SpringFramework中提供很多實用功能,如:依賴注入、事務管理、web開發支持、數據訪問、消息服務等等。
而如果我們在項目中,直接基于SpringFramework進行開發,存在兩個問題:
-
配置繁瑣
-
入門難度大
所以基于此呢,spring官方推薦我們從另外一個項目開始學習,那就是目前最火爆的SpringBoot。 通過springboot就可以快速的幫我們構建應用程序,所以springboot呢,最大的特點有兩個 :
-
簡化配置
-
快速開發
Spring Boot 可以幫助我們非常快速的構建應用程序、簡化開發、提高效率 。
而直接基于SpringBoot進行項目構建和開發,不僅是Spring官方推薦的方式,也是現在企業開發的主流。
1.2 入門程序
1.2.1 需求
需求:基于SpringBoot的方式開發一個web應用,瀏覽器發起請求/hello后,給瀏覽器返回字符串 “Hello xxx ~”。
1.2.2 開發步驟
第1步:創建SpringBoot工程,并勾選Web開發相關依賴
第2步:定義HelloController類,添加方法hello,并添加注解
1). 創建SpringBoot工程(需要聯網)
基于Spring官方骨架,創建SpringBoot工程。
基本信息描述完畢之后,勾選web開發相關依賴。
SpringBoot官方提供的腳手架,里面只能夠選擇SpringBoot的幾個最新的版本,如果要選擇其他相對低一點的版本,可以在springboot項目創建完畢之后,修改項目的pom.xml文件中的版本號。
點擊Create之后,就會聯網創建這個SpringBoot工程,創建好之后,結構如下:
注意:在聯網創建過程中,會下載相關資源(請耐心等待)
2). 定義HelloController類,添加方法hello,并添加注解
在com.itheima
這個包下新建一個類:HelloController
HelloController中的內容,具體如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController//表示當前類是一個請求處理類
public class HelloController {@RequestMapping("/hello") //請求路徑"/hello",和需求保持一致,參數名就是namepublic String hello(String name){System.out.println("name : " + name);//前端響應數據return "Hello" + name + "~";}
}
3). 運行測試
運行SpringBoot自動生成的引導類 (標識有@SpringBootApplication
注解的類),注意一般Debug運行,出現問題方便排查
打開瀏覽器,輸入 http://localhost:8080/hello?name=itheima
(請求路徑 + ?+ 請求參數)
1.2.3 常見問題
大伙兒在下來聯系的時候,聯網基于spring的腳手架創建SpringBoot項目,偶爾可能會因為網內網絡的原因,鏈接不上SpringBoot的腳手架網站,此時會出現如下現象:
此時可以使用阿里云提供的腳手架,網址為:https://start.aliyun.com
然后按照項目創建的向導,一步一步的創建項目即可。
端口占用問題
網頁打開報Access Error:404 – Not Found Cannot open document for:/的錯誤,經查看,是tomcat服務器的8080端口被占用了,找到被占用的程序,將其關掉即可。
首先,打開cmd窗口,使用netstat -ano | find ":8080"
,列出與8080相關的,最后一列是其PID
taskkill /PID [PID] /F
命令來強制結束進程(將[PID]替換為實際的進程ID)。
1.3 入門解析
那在上面呢,我們已經完成了SpringBootWeb的入門程序,并且測試通過。 在入門程序中,我們發現,我們只需要一個main方法就可以將web應用啟動起來了,然后就可以打開瀏覽器訪問了。
那接下來我們需要明確兩個問題:
1). 為什么一個main方法就可以將Web應用啟動了?
因為我們在創建springboot項目的時候,選擇了web開發的起步依賴 spring-boot-starter-web
(WEB開發依賴)。而spring-boot-starter-web
依賴,又依賴了spring-boot-starter-tomcat
,由于maven的依賴傳遞特性,那么在我們創建的springboot項目中也就已經有了tomcat的依賴,這個其實就是springboot中內嵌的tomcat。
而我們運行引導類中的main方法,其實啟動的就是springboot中內嵌的Tomcat服務器。 而我們所開發的項目,也會自動的部署在該tomcat服務器中,并占用8080端口號 。
起步依賴:
Tomcat
什么是Tomcat? Tomcat是一個開源、免費、輕量級的Web服務器。 Tomcat是Apache 軟件基金會(Apache Software Foundation)的Jakarta 項目中的一個核心項目,由Apache、Sun 和其他一些公司及個人共同開發而成。由于有了Sun 的參與和支持,最新的Servlet 和JSP 規范總是能在Tomcat 中得到體現,Tomcat 5支持最新的…
2. HTTP協議
2.1 HTTP概述
2.1.1 介紹
HTTP:Hyper Text Transfer Protocol(超文本傳輸協議),規定了瀏覽器與服務器之間數據傳輸的規則。
-
http是互聯網上應用最為廣泛的一種網絡協議
-
超文本是一種文本的結構形式,其中的文本內容不僅僅是線性排列的,而是通過鏈接(通常稱為“超鏈接”)與其他文本、圖像、音頻、視頻或不同類型的內容相關聯。用戶可以通過點擊這些鏈接從一個部分跳轉到另一個部分,形成一種非線性、互動的閱讀體驗。
-
http協議要求:瀏覽器在向服務器發送請求數據時,或是服務器在向瀏覽器發送響應數據時,都必須按照固定的格式進行數據傳輸
如何讀得懂瀏覽器發送的請求呢?固定的格式 --> http協議規定
如果想知道http協議的數據傳輸格式有哪些,可以打開瀏覽器,點擊F12
打開開發者工具,點擊Network(網絡)
來查看
瀏覽器向服務器進行請求時,服務器按照固定的格式進行解析:都是純文本
服務器向瀏覽器進行響應時,瀏覽器按照固定的格式進行解析:
而我們學習HTTP協議,就是來學習請求和響應數據的具體格式內容。
2.1.2 特點
我們剛才初步認識了HTTP協議,那么我們在看看HTTP協議有哪些特點:
- 基于TCP協議: 面向連接,安全
TCP是一種面向連接的(建立連接之前是需要經過三次握手)、可靠的、基于字節流的傳輸層通信協議,在數據傳輸方面更安全
- 基于請求-響應模型: 一次請求對應一次響應(先請求后響應)
請求和響應是一一對應關系,沒有請求,就沒有響應
- HTTP協議是無狀態協議: 對于數據沒有記憶能力。每次請求-響應都是獨立的
無狀態指的是客戶端發送HTTP請求給服務端之后,服務端根據請求響應數據,響應完后,不會記錄任何信息。
請求之間無法共享數據會引發的問題:
如:京東購物。加入購物車和去購物車結算是兩次請求
由于HTTP協議的無狀態特性,加入購物車請求響應結束后,并未記錄加入購物車是何商品
發起去購物車結算的請求后,因為無法獲取哪些商品加入了購物車,會導致此次請求無法正確展示數據
具體使用的時候,我們發現京東是可以正常展示數據的,原因是Java早已考慮到這個問題,并提出了使用會話技術(Cookie、Session)來解決這個問題。具體如何來做,我們后面課程中會講到。
剛才提到HTTP協議是規定了請求和響應數據的格式,那具體的格式是什么呢? 接下來,我們就來詳細剖析。
HTTP協議又分為:請求協議和響應協議
2.2 HTTP請求協議
2.2.1 介紹
-
請求協議:瀏覽器將數據以請求格式發送到服務器。包括:請求行、請求頭 、請求體
-
GET方式的請求協議:
-
請求行(以上圖中紅色部分) :HTTP請求中的第一行數據。由:
請求方式
、資源路徑
、協議/版本
組成(之間使用空格分隔)-
請求方式:GET , GET請求方式沒有請求體,請求數據是在請求行中,請求大小有限制
-
資源路徑:/brand/findAll?name=OPPO&status=1
-
請求路徑:/brand/findAll
-
請求參數:name=OPPO&status=1
-
請求參數是以key=value形式出現
-
多個請求參數之間使用
&
連接
-
-
請求路徑和請求參數之間使用
?
連接
-
-
協議/版本:HTTP/1.1
-
-
請求頭(以上圖中黃色部分) :第二行開始,上圖黃色部分內容就是請求頭。格式為key: value形式
-
http是個無狀態的協議,所以在請求頭設置瀏覽器的一些自身信息和想要響應的形式。這樣服務器在收到信息后,就可以知道是誰,想干什么了
-
常見的HTTP請求頭有:
請求頭 含義 Host 表示請求的主機名 User-Agent 瀏覽器版本。 例如:Chrome瀏覽器的標識類似Mozilla/5.0 …Chrome/79 ,IE瀏覽器的標識類似Mozilla/5.0 (Windows NT …)like Gecko Accept 表示瀏覽器能接收的資源類型,如text/*,image/*或者*/*表示所有; Accept-Language 表示瀏覽器偏好的語言,服務器可以據此返回不同語言的網頁; Accept-Encoding 表示瀏覽器可以支持的壓縮類型,例如gzip, deflate等。 Content-Type 請求主體的數據類型 Content-Length 數據主體的大小(單位:字節)
-
舉例說明:服務端可以根據請求頭中的內容來獲取客戶端的相關信息,有了這些信息服務端就可以處理不同的業務需求。
比如:
不同瀏覽器解析HTML和CSS標簽的結果會有不一致,所以就會導致相同的代碼在不同的瀏覽器會出現不同的效果
服務端根據客戶端請求頭中的數據獲取到客戶端的瀏覽器類型,就可以根據不同的瀏覽器設置不同的代碼來達到一致的效果(這就是我們常說的瀏覽器兼容問題)
-
請求體 :存儲請求參數
- GET請求的請求參數在請求行中,故不需要設置請求體
POST方式的請求協議:
-
請求行(以上圖中紅色部分):包含請求方式、資源路徑、協議/版本
-
請求方式:POST
-
資源路徑:/brand
-
協議/版本:HTTP/1.1
-
-
請求頭(以上圖中黃色部分)
-
請求體(以上圖中綠色部分) :存儲請求參數
- 請求體和請求頭之間是有一個空行隔開(作用:用于標記請求頭結束)
GET請求和POST請求的區別:
區別方式 | GET請求 | POST請求 |
---|---|---|
請求參數 | 請求參數在請求行中。<br/>例:/brand/findAll?name=OPPO&status=1 | 請求參數在請求體中 |
請求參數長度 | 請求參數長度有限制(瀏覽器不同限制也不同) | 請求參數長度沒有限制 |
安全性 | 安全性低。原因:請求參數暴露在瀏覽器地址欄中。 | 安全性相對高 |
2.2.2 獲取請求數據
Web服務器(Tomcat)對HTTP協議的請求數據進行解析,并進行了封裝(HttpServletRequest對象),并在調用Controller方法的時候傳遞給了該方法。這樣,就使得程序員不必直接對協議進行操作,讓Web開發更加便捷。
代碼演示如下:
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RequestController {@RequestMapping("/request")public String request(HttpServletRequest request){//1.獲取請求方式String method = request.getMethod();System.out.println("請求方式 : " + method);//2.獲取請求url地址String url = request.getRequestURL().toString();System.out.println("請求url : " + url);String uri = request.getRequestURI();System.out.println("請求uri : " + uri);//3.獲取請求協議String protocol = request.getProtocol();System.out.println("請求協議 : " + protocol);//4.獲取請求參數 - nameString name = request.getParameter("name");String age = request.getParameter("age");System.out.println("請求參數 - name : " + name + " , age : " + age);//5.獲取請求頭 - AcceptString accept = request.getHeader("Accept");System.out.println( "請求頭 - Accept : " + accept);return "OK";}
}
最終輸出內容如下所示:
2.3 HTTP響應協議
2.3.1 格式介紹
- 響應協議:服務器將數據以響應格式返回給瀏覽器。包括:響應行 、響應頭 、響應體
-
響應行(以上圖中紅色部分):響應數據的第一行。響應行由
協議及版本
、響應狀態碼
、狀態碼描述
組成-
協議/版本:HTTP/1.1
-
響應狀態碼:200
-
狀態碼描述:OK
-
-
響應頭(以上圖中黃色部分):響應數據的第二行開始。格式為key:value形式
-
http是個無狀態的協議,所以可以在請求頭和響應頭中設置一些信息和想要執行的動作,這樣,對方在收到信息后,就可以知道你是誰,你想干什么
-
常見的HTTP響應頭有:
Content-Type:表示該響應內容的類型,例如text/html,image/jpeg ;Content-Length:表示該響應內容的長度(字節數);Content-Encoding:表示該響應壓縮算法,例如gzip ;Cache-Control:指示客戶端應如何緩存,例如max-age=300表示可以最多緩存300秒 ;Set-Cookie: 告訴瀏覽器為當前頁面所在的域設置cookie ;
-
-
響應體(以上圖中綠色部分): 響應數據的最后一部分。存儲響應的數據
- 響應體和響應頭之間有一個空行隔開(作用:用于標記響應頭結束)
2.3.2 響應狀態碼
關于響應狀態碼,我們先主要認識三個狀態碼,其余的等后期用到了再去掌握:
-
200 ok
客戶端請求成功 -
404 Not Found
請求資源不存在 -
500 Internal Server Error
服務端發生不可預期的錯誤
2.3.3 設置響應數據
Web服務器對HTTP協議的響應數據進行了封裝(HttpServletResponse),并在調用Controller方法的時候傳遞給了該方法。這樣,就使得程序員不必直接對協議進行操作,讓Web開發更加便捷。
代碼演示:
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestController
public class ResponseController {@RequestMapping("/response")public void response(HttpServletResponse response) throws IOException {/*方式一:HttpServletResponse設置響應數據*///1. 設置響應狀態碼//response.setStatus(HttpServletResponse.SC_OK);response.setStatus(401);//2. 設置響應頭response.setHeader("name", "xuner");//3. 設置響應體 ?response.getWriter().write("<h1>hello response</h1>");}/** 方式二:用Spring提供的* */@RequestMapping("/response2")public ResponseEntity<String> response2(){return ResponseEntity.status(401).header("name", "xuner").body("<h1>hello response</h1>");}}
瀏覽器訪問測試:
響應狀態碼 和 響應頭如果沒有特殊要求的話,通常不手動設定。服務器會根據請求處理的邏輯,自動設置響應狀態碼和響應頭。
3. SpringBootWeb案例
3.1 需求說明
需求:基于SpringBoot開發web程序,完成用戶列表的渲染展示
當在瀏覽器地址欄,訪問前端靜態頁面(http://localhost:8080/usre.html)后,在前端頁面上,會發送ajax請求,請求服務端(http://localhost:8080/list),服務端程序加載 user.txt 文件中的數據,讀取出來后最終給前端頁面響應json格式的數據,前端頁面再將數據渲染展示在表格中。
3.2 代碼實現
1). 準備工作:再創建一個SpringBoot工程,并勾選web依賴、lombok依賴。
2). 準備工作:引入資料中準備好的數據文件user.txt,以及static下的前端靜態頁面
這些文件,在提供的資料中,已經提供了直接導入進來即可。
3). 準備工作:定義封裝用戶信息的實體類。
在 com.itheima
下再定義一個包 pojo
,專門用來存放實體類。 在該包下定義一個實體類User:
package com.itheima.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;/*** 封裝用戶信息*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String username;private String password;private String name;private Integer age;private LocalDateTime updateTime;
}
3). 開發服務端程序,接收請求,讀取文本數據并響應
由于在案例中,需要讀取文本中的數據,并且還需要將對象轉為json格式,所以這里呢,我們在項目中再引入一個非常常用的工具包hutool。 然后調用里面的工具類,就可以非常方便快捷的完成業務操作。
pom.xml
中引入依賴
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.27</version>
</dependency>
- 在
com.itheima
包下新建一個子包controller
,在其中創建一個UserController
package com.cyanm.controller;
import cn.hutool.core.io.IoUtil;
import com.cyanm.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/*** 用戶信息接收請求*/
@RestController
public class UserController {//Controller返回值就是前端要相應的數據@RequestMapping("/list")public List<User> list() throws Exception {//1. 加載并讀取user.txt獲得用戶數據,封裝至linesInputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, "UTF-8", new ArrayList<>());//2. 解析用戶信息 將用戶封裝為User對象 --> 封裝為一個list集合List<User> userList= lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String userName = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));//將字符串解析為LocalDateTimereturn new User(id,userName,password,name,age,updateTime);}).toList();//3. 將list集合數據轉換為json返回return userList; //最后服務器會自動轉換為json格式返回,再響應給前端}}
代碼解讀
包引用
cn.hutool.core.io.IoUtil
:從Hutool工具庫導入的類,可以幫助我們處理文件讀取。Hutool 是一個非常流行的 Java 工具集,旨在簡化開發中的常見任務,比如文件操作、日期處理、集合管理等。它的設計理念是“簡單、快速、實用”,包含了很多模塊,而IoUtil
屬于 IO(輸入/輸出)模塊,專門用來處理與輸入輸出相關的操作。api文檔:Hutool教程文檔
org.springframework.web.bind.annotation.RequestMapping
:Spring框架的工具,用于指定URL路徑。
org.springframework.web.bind.annotation.RestController
:Spring框架的工具,標記這是一個處理網頁請求的類。
java.io.InputStream
:Java自帶的類,用于讀取文件內容。
java.time.LocalDateTime
:Java自帶的類,表示日期和時間。
@RestController
這是一個Spring框架的注解,告訴Java:“這個類是用來處理網頁請求的,而且返回值會自動變成JSON格式。@RestController
是@Controller
和@ResponseBody
的組合,專門用于RESTful風格的API(簡單說,就是給前端返回數據
@RequestMapping("/list")
:告訴Spring,當用戶訪問
/list
這個網頁地址時,調用下面的方法。解釋:
@RequestMapping
是Spring的注解,/list
是URL路徑,比如http://localhost:8080/list
。
public List<User> list() throws Exception {
:
- 作用:定義一個方法,名字叫
list
,返回值是一個List<User>
(用戶對象的列表)。
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
this.getClass()
:獲取當前類的信息(UserController
)。
getClass()
:這是 Java 中每個對象都有的方法,調用它會返回這個對象的類信息。類信息用一個Class
對象表示。簡單說,如果this
是UserController
的實例,那么this.getClass()
就返回UserController.class
.作用是獲取當前對象的類信息。那么,這個“類信息”具體是什么呢?簡單來說,它是一個Class
對象,代表了當前對象所屬的類的“身份卡”或者“藍圖”。這個“身份卡”包含了關于類的所有元數據(metadata),比如類的名字、所在的包、方法、字段等。
getClassLoader()
:拿到類的“加載器”,負責找資源類加載器是什么? 在 Java 中,類加載器是一個幕后英雄,負責把類和資源(比如文件、圖片)從磁盤、JAR 包或其他地方加載到程序中運行。每個類都有一個類加載器,幫它完成“搬運”工作。
getResourceAsStream("user.txt")
:從類路徑(通常是src/main/resources
目錄)加載user.txt
文件,返回一個輸入流(InputStream
)。
getResourceAsStream(String name)
:這是ClassLoader
類提供的方法,告訴類加載器去類路徑(classpath
)中找一個名叫name
的資源(文件),然后返回一個可以讀取這個資源的輸入流
ArrayList<String> lines = IoUtil.readLines(in, "UTF-8", new ArrayList<>());
作用:把
user.txt
文件的內容按行讀出來,存到一個列表lines
里。解釋:
IoUtil.readLines
:Hutool工具的方法,專門讀取輸入流。in
:剛拿到的輸入流,里面是user.txt
的內容。"UTF-8"
:文件編碼方式,防止中文亂碼。new ArrayList<>()
:創建一個空的列表,把讀到的每一行放進去。lines
:結果是一個ArrayList<String>
,每行是一個字符串。
List<User> userList = lines.stream().map(line -> {
作用:把
lines
里的每一行字符串變成User
對象,存到userList
里。
.map(line -> { ... })
:對流水線上的每行(line
)做處理,變成新的東西(User
對象)。Stream 是什么?
- Stream 是 Java 8 引入的一個工具,想象它是一個“流水線”。它可以把集合(比如列表)里的數據放上傳送帶,讓你對每個數據進行加工。
- 和列表不同,Stream 不是用來存數據的,而是用來處理數據的。
lines.stream()
:
lines
是一個ArrayList<String>
,里面裝著很多字符串。
.stream()
方法把這個列表變成一個 Stream,Stream 里裝著和lines
一樣的元素(每一行文本),但現在它們可以被“流水線”加工了。假設
lines
是:["1,admin,123", "2,user,456"]
lines.stream()
就是把這倆字符串放進一個流水線,等待下一步操作。map
.map
是 Stream 的一個方法,意思是“映射”或“轉換”。它會遍歷 Stream 里的每個元素,應用一個規則(函數),把舊元素變成新元素,然后生成一個新的 Stream。
line -> { … }:
這是一個 Lambda 表達式,告訴
.map
如何把一個line
(字符串)變成一個User
對象。
line
是流水線上的當前元素(比如"1,admin,123456,張三,25,2023-10-01 12:00:00"
)。
{ ... }
里面是具體的轉換邏輯。
String[] parts = line.split(",");
作用:把每行字符串按逗號
,
分開,存到數組parts
里。解釋:
line
:流水線當前處理的某一行,比如"1,admin,123456,張三,25,2023-10-01 12:00:00"
。split(",")
:按逗號切開,變成數組:["1", "admin", "123456", "張三", "25", "2023-10-01 12:00:00"]
。
Integer id = Integer.parseInt(parts[0]);
:
parts[0]
是"1"
,用Integer.parseInt
把字符串"1"
變成整數1
。Integer
是整數類型。
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
:
parts[5]
是"2023-10-01 12:00:00"
。DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
:定義日期格式(年-月-日 時:分:秒)。LocalDateTime.parse
:把字符串按格式解析成日期時間對象。
return new User(id, userName, password, name, age, updateTime);
- 用這些字段創建一個
User
對象并返回。
}).toList();
作用:把流水線上的所有
User
對象收集成一個List<User>
。解釋:
map
處理完每行后,流水線里是一堆User
對象。toList()
把它們裝進一個列表。
4). 啟動服務測試,訪問:http://localhost:8080/user.html
為什么服務器能自動轉換為JSON??????原因就在于RestController中封裝了一個ResponseBody
3.3 @ResponseBody
前面我們學習過HTTL協議的交互方式:請求響應模式(有請求就有響應)。那么Controller程序呢,除了接收請求外,還可以進行響應。
在我們前面所編寫的controller方法中,都已經設置了響應數據。
controller方法中的return的結果,怎么就可以響應給瀏覽器呢?
答案:使用@ResponseBody注解
@ResponseBody注解:
-
類型:方法注解、類注解
-
位置:書寫在Controller方法上或類上
- 作用:將方法返回值直接響應給瀏覽器(前端),如果返回值類型是實體對象/集合,將會轉換為JSON格式后在響應給瀏覽器
但是在我們所書寫的Controller中,只在類上添加了@RestController注解、方法添加了@RequestMapping注解,并沒有使用@ResponseBody注解,怎么給瀏覽器響應呢?
這是因為,我們在類上加了@RestController注解,而這個注解是由兩個注解組合起來的,分別是:@Controller 、@ResponseBody。 那也就意味著,我們在類上已經添加了@ResponseBody注解了,而一旦在類上加了@ResponseBody注解,就相當于該類所有的方法中都已經添加了@ResponseBody注解。
提示:前后端分離的項目中,一般直接在請求處理類上加@RestController注解,就無需在方法上加@ResponseBody注解了。
3.4 問題分析
上述案例的功能,我們雖然已經實現,但是呢,我們會發現案例中:解析文本文件中的數據,處理數據的邏輯代碼,給頁面響應的代碼全部都堆積在一起了,全部都寫在controller方法中了。
當前程序的這個業務邏輯還是比較簡單的,如果業務邏輯再稍微復雜一點,我們會看到Controller方法的代碼量就很大了。
-
當我們要修改操作數據部分的代碼,需要改動Controller
-
當我們要完善邏輯處理部分的代碼,需要改動Controller
-
當我們需要修改數據響應的代碼,還是需要改動Controller
這樣呢,就會造成我們整個工程代碼的復用性比較差,而且代碼難以維護。 那如何解決這個問題呢?其實在現在的開發中,有非常成熟的解決思路,那就是分層開發。
4. 分層解耦
4.1 三層架構
4.1.1 介紹
在我們進行程序設計以及程序開發時,盡可能讓每一個接口、類、方法的職責更單一些(單一職責原則)。
單一職責原則:一個類或一個方法,就只做一件事情,只管一塊功能。
這樣就可以讓類、接口、方法的復雜度更低,可讀性更強,擴展性更好,也更利于后期的維護。
我們之前開發的程序呢,并不滿足單一職責原則。下面我們來分析下之前的程序:
那其實我們上述案例的處理邏輯呢,從組成上看可以分為三個部分:
-
數據訪問:負責業務數據的維護操作,包括增、刪、改、查等操作。
-
邏輯處理:負責業務邏輯處理的代碼。
-
請求處理、響應數據:負責,接收頁面的請求,給頁面響應數據。
按照上述的三個組成部分,在我們項目開發中呢,可以將代碼分為三層,如圖所示:
-
Controller:控制層。接收前端發送的請求,對請求進行處理,并響應數據。
-
Service:業務邏輯層。處理具體的業務邏輯。
-
Dao:數據訪問層(Data Access Object),也稱為持久層。負責數據訪問操作,包括數據的增、刪、改、查。
基于三層架構的程序執行流程,如圖所示:
-
前端發起的請求,由Controller層接收(Controller響應數據給前端)
-
Controller層調用Service層來進行邏輯處理(Service層處理完后,把處理結果返回給Controller層)
-
Serivce層調用Dao層(邏輯處理過程中需要用到的一些數據要從Dao層獲取)
-
Dao層操作文件中的數據(Dao拿到的數據會返回給Service層)
思考:按照三層架構的思想,如果要對業務邏輯(Service層)進行變更,會影響到Controller層和Dao層嗎?
答案:不會影響。 (程序的擴展性、維護性變得更好了)
4.1.2 代碼拆分
我們使用三層架構思想,來改造下之前的程序:
-
控制層包名:
com.itheima.controller
-
業務邏輯層包名:
com.itheima.service
-
數據訪問層包名:
com.itheima.dao
1). 控制層:接收前端發送的請求,對請求進行處理,并響應數據
在 com.itheima.controller
中創建UserController類,代碼如下:
package com.itheima.controller;import com.itheima.pojo.User;
import com.itheima.service.UserService;
import com.itheima.service.impl.UserServiceImpl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;@RestController
public class UserController {private UserService userService = new UserServiceImpl();@RequestMapping("/list")public List<User> list(){//1.調用ServiceList<User> userList = userService.findAll();//2.響應數據return userList;}}
2). 業務邏輯層:處理具體的業務邏輯
在 com.itheima.service
中創建UserSerivce接口,代碼如下:
package com.itheima.service;import com.itheima.pojo.User;
import java.util.List;public interface UserService {public List<User> findAll();}
在 com.itheima.service.impl
中創建UserSerivceImpl接口,代碼如下:
package com.itheima.service.impl;import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.pojo.User;
import com.itheima.service.UserService;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl();@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();List<User> userList = lines.stream().map(line -> {String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).collect(Collectors.toList());return userList;}
}
3). 數據訪問層:負責數據的訪問操作,包含數據的增、刪、改、查
在 com.itheima.dao
中創建UserDao接口,代碼如下:
package com.itheima.dao;import java.util.List;public interface UserDao {public List<String> findAll();}
在 com.itheima.dao.impl
中創建UserDaoImpl接口,代碼如下:
package com.itheima.dao.impl;import cn.hutool.core.io.IoUtil;
import com.itheima.dao.UserDao;import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;public class UserDaoImpl implements UserDao {@Overridepublic List<String> findAll() {InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());return lines;}
}
具體的請求調用流程:
三層架構的好處:
復用性強
便于維護
利用擴展
4.2 分層解耦
4.2.1 問題分析
由于我們現在在程序中,需要什么對象,直接new一個對象 new UserServiceImpl()
。
如果說我們需要更換實現類,比如由于業務的變更,UserServiceImpl 不能滿足現有的業務需求,我們需要切換為 UserServiceImpl2 這套實現,就需要修改Contorller的代碼,需要創建 UserServiceImpl2 的實現new UserServiceImpl2()
。
Service中調用Dao,也是類似的問題。這種呢,我們就稱之為層與層之間 耦合 了。 那什么是耦合呢 ?
首先需要了解軟件開發涉及到的兩個概念:內聚和耦合。
-
內聚:軟件中各個功能模塊內部的功能聯系。
-
耦合:衡量軟件中各個層/模塊之間的依賴、關聯的程度。
軟件設計原則:高內聚低耦合。
高內聚:指的是一個模塊中各個元素之間的聯系的緊密程度,如果各個元素(語句、程序段)之間的聯系程度越高,則內聚性越高,即 “高內聚”。
低耦合:指的是軟件中各個層、模塊之間的依賴關聯程序越低越好。
目前層與層之間是存在耦合的,Controller耦合了Service、Service耦合了Dao。而 高內聚、低耦合的目的是使程序模塊的可重用性、移植性大大增強。
那最終我們的目標呢,就是做到層與層之間,盡可能的降低耦合,甚至解除耦合。
4.2.2 解耦思路
之前我們在編寫代碼時,需要什么對象,就直接new一個就可以了。 這種做法呢,層與層之間代碼就耦合了,當service層的實現變了之后, 我們還需要修改controller層的代碼。
那應該怎么解耦呢?
1). 首先不能在EmpController中使用new對象。代碼如下:
此時,就存在另一個問題了,不能new,就意味著沒有業務層對象(程序運行就報錯),怎么辦呢?
我們的解決思路是:
-
提供一個容器,容器中存儲一些對象(例:UserService對象)
-
Controller程序從容器中獲取UserService類型的對象
2). 將要用到的對象交給一個容器管理。
3). 應用程序中用到這個對象,就直接從容器中獲取
那問題來了,我們如何將對象交給容器管理呢? 程序運行時,容器如何為程序提供依賴的對象呢?
我們想要實現上述解耦操作,就涉及到Spring中的兩個核心概念:
控制反轉: Inversion Of Control,簡稱IOC。對象的創建控制權由程序自身轉移到外部(容器),這種思想稱為控制反轉。
對象的創建權由程序員主動創建轉移到容器(由容器創建、管理對象)。這個容器稱為:IOC容器或Spring容器。
依賴注入: Dependency Injection,簡稱DI。容器為應用程序提供運行時,所依賴的資源,稱之為依賴注入。
程序運行時需要某個資源,此時容器就為其提供這個資源。
例:EmpController程序運行時需要EmpService對象,Spring容器就為其提供并注入EmpService對象。
bean對象:IOC容器中創建、管理的對象,稱之為:bean對象。
4.3 IOC&DI入門
1). 將Service及Dao層的實現類,交給IOC容器管理
在實現類加上 @Component
注解,就代表把當前類產生的對象交給IOC容器管理。
A. UserDaoImpl
@Component
public class UserDaoImpl implements UserDao {@Overridepublic List<String> findAll() {InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());return lines;}
}
B. UserServiceImpl
@Component
public class UserServiceImpl implements UserService {private UserDao userDao;@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();List<User> userList = lines.stream().map(line -> {String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).collect(Collectors.toList());return userList;}
}
2). 為Controller 及 Service注入運行時所依賴的對象
A. UserServiceImpl
@Component
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;//程序自動從容器中找到對應的bean對象,并且賦值給成員變量@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();List<User> userList = lines.stream().map(line -> {String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).collect(Collectors.toList());return userList;}
}
B. UserController
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/list")public List<User> list(){//1.調用ServiceList<User> userList = userService.findAll();//2.響應數據return userList;}}
啟動服務,運行測試。 打開瀏覽器,地址欄直接訪問:http://localhost:8080/user.html 。 依然正常訪問,就說明入門程序完成了。 已經完成了層與層之間的解耦。
4.4 IOC詳解
通過IOC和DI的入門程序呢,我們已經基本了解了IOC和DI的基礎操作。接下來呢,我們學習下IOC控制反轉和DI依賴注入的細節。
4.4.1 Bean的聲明
前面我們提到IOC控制反轉,就是將對象的控制權交給Spring的IOC容器,由IOC容器創建及管理對象。IOC容器創建的對象稱為bean對象。
在之前的入門案例中,要把某個對象交給IOC容器管理,需要在類上添加一個注解:@Component
而Spring框架為了更好的標識web應用程序開發當中,bean對象到底歸屬于哪一層,又提供了@Component的衍生注解:
注解 | 說明 | 位置 |
---|---|---|
@Component | 聲明bean的基礎注解 | 不屬于以下三類時,用此注解 |
@Controller | @Component的衍生注解 | 標注在控制層類上 |
@Service | @Component的衍生注解 | 標注在業務層類上 |
@Repository | @Component的衍生注解 | 標注在數據訪問層類上(由于與mybatis整合,用的少) |
那么此時,我們就可以使用 @Service
注解聲明Service層的bean。 使用 @Repository
注解聲明Dao層的bean。 代碼實現如下:
Service層:
@Service
public class UserServiceImpl implements UserService {private UserDao userDao;@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();List<User> userList = lines.stream().map(line -> {String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).collect(Collectors.toList());return userList;}
}
Dao層:
@Repository
public class UserDaoImpl implements UserDao {@Overridepublic List<String> findAll() {InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());return lines;}
}
注意1:聲明bean的時候,可以通過注解的value屬性指定bean的名字,如果沒有指定,默認為類名首字母小寫。
注意2:使用以上四個注解都可以聲明bean,但是在springboot集成web開發中,聲明控制器bean只能用@Controller。
4.4.2 組件掃描
問題:使用前面學習的四個注解聲明的bean,一定會生效嗎?
答案:不一定。(原因:bean想要生效,還需要被組件掃描)
-
前面聲明bean的四大注解,要想生效,還需要被組件掃描注解
@ComponentScan
掃描。 -
該注解雖然沒有顯式配置,但是實際上已經包含在了啟動類聲明注解
@SpringBootApplication
中,默認掃描的范圍是啟動類所在包及其子包。

所以,我們在項目開發中,只需要按照如上項目結構,將項目中的所有的業務類,都放在啟動類所在包的子包中,就無需考慮組件掃描問題。
4.5 DI詳解
上一小節我們講解了控制反轉IOC的細節,接下來呢,我們學習依賴注解DI的細節。
依賴注入,是指IOC容器要為應用程序去提供運行時所依賴的資源,而資源指的就是對象。
在入門程序案例中,我們使用了@Autowired這個注解,完成了依賴注入的操作,而這個Autowired翻譯過來叫:自動裝配。
@Autowired
注解,默認是按照類型進行自動裝配的(去IOC容器中找某個類型的對象,然后完成注入操作)
入門程序舉例:在EmpController運行的時候,就要到IOC容器當中去查找EmpService這個類型的對象,而我們的IOC容器中剛好有一個EmpService這個類型的對象,所以就找到了這個類型的對象完成注入操作。
4.5.1 @Autowired用法
@Autowired 進行依賴注入,常見的方式,有如下三種:
1). 屬性注入
@RestController
public class UserController {//方式一: 屬性注入@Autowiredprivate UserService userService;}
-
優點:代碼簡潔、方便快速開發。
-
缺點:隱藏了類之間的依賴關系、可能會破壞類的封裝性。(依賴了UserService,但是在類的結構層面并沒有關聯UserService,并且對當前變量,并沒有提供get和set方法,而是在底層用反射來對當前變量賦值)
2). 構造函數注入
@RestController
public class UserController {//方式二:構造器注入private final UserService userService;//@Autowired //如果當前類中構造函數只有一個,Autowired可以省略,啟動之后,會自動到IOC容器中查找UserService類型的bean對象,并進行賦值@Autowiredpublic UserController(UserService userService) {this.userService = userService;}}
-
優點:能清晰地看到類的依賴關系、提高了代碼的安全性(final靜態成員變量)。
-
缺點:代碼繁瑣、如果構造參數過多,可能會導致構造函數臃腫。
-
注意:如果只有一個構造函數,@Autowired注解可以省略。(通常來說,也只有一個構造函數)
3). setter注入
/*** 用戶信息Controller*/
@RestController
public class UserController {//方式三: setter注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}}
-
優點:保持了類的封裝性,對外提供了公共的set方法,依賴關系更清晰。
-
缺點:需要額外編寫setter方法,增加了代碼量,有多個bean就要提供多個setter方法。
在項目開發中,基于@Autowired進行依賴注入時,基本都是第一種和第二種方式。(官方推薦第二種方式,因為會更加規范)但是在企業項目開發中,很多的項目中,也會選擇第一種方式因為更加簡潔、高效(在規范性方面進行了妥協)。
4.5.2 注意事項
那如果在IOC容器中,存在多個相同類型的bean對象,會出現什么情況呢?
在下面的例子中,我們準備了兩個UserService的實現類,并且都交給了IOC容器管理。 代碼如下:
此時,我們啟動項目會發現,控制臺報錯了:
出現錯誤的原因呢,是因為在Spring的容器中,UserService這個類型的bean存在兩個,框架不知道具體要注入哪個bean使用,所以就報錯了。
如何解決上述問題呢?Spring提供了以下幾種解決方案:
-
@Primary
-
@Qualifier
-
@Resource
方案一:使用@Primary注解
當存在多個相同類型的Bean注入時,加上@Primary注解,來確定默認的實現,優先使用這個,也就是提升優先級。
@Primary
@Service
public class UserServiceImpl implements UserService {
}
方案二:使用@Qualifier注解
指定當前要注入的bean對象。 在@Qualifier的value屬性中,指定注入的bean的名稱。 @Qualifier注解不能單獨使用,必須配合@Autowired使用。
@RestController
public class UserController {@Qualifier("userServiceImpl") //注意默認bean名是類名首字母小寫@Autowiredprivate UserService userService;
方案三:使用@Resource注解
是按照bean的名稱進行注入。通過name屬性指定要注入的bean的名稱。
@RestController
public class UserController {@Resource(name = "userServiceImpl")private UserService userService;
面試題:@Autowird 與 @Resource的區別
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默認是按照類型注入,而@Resource是按照名稱注入
附錄:常見狀態碼
狀態碼 | 狀態碼分類 | 解釋 |
---|---|---|
100 Continue | 信息響應 | 表示服務器收到了請求的初始部分,并且客戶端應該繼續發送請求的剩余部分。 |
101 Switching Protocols | 信息響應 | 表示服務器正在根據客戶端的請求切換協議。 |
200 OK | 成功響應 | 表示請求成功,并且服務器返回了請求的數據。 |
201 Created | 成功響應 | 表示請求成功,并且服務器創建了一個新的資源。 |
202 Accepted | 成功響應 | 表示請求已經被接受,但是服務器尚未處理完成。 |
203 Non-Authoritative Information | 成功響應 | 表示服務器成功處理了請求,但是返回的信息不是來自原始服務器。 |
204 No Content | 成功響應 | 表示請求成功,但是服務器沒有返回任何數據。 |
205 Reset Content | 成功響應 | 表示請求成功,并且客戶端應該重置文檔視圖。 |
206 Partial Content | 成功響應 | 表示服務器成功處理了部分請求,并且返回了部分數據。 |
300 Multiple Choices | 重定向響應 | 表示請求的資源有多個選項,客戶端應該選擇一個。 |
301 Moved Permanently | 重定向響應 | 表示請求的資源已經被永久移動到新的位置。 |
302 Found | 重定向響應 | 表示請求的資源已經被臨時移動到新的位置。 |
303 See Other | 重定向響應 | 表示請求的資源可以在另一個URI下找到。 |
304 Not Modified | 重定向響應 | 表示請求的資源自上次請求以來沒有被修改。 |
307 Temporary Redirect | 重定向響應 | 表示請求的資源已經被臨時重定向到新的位置。 |
308 Permanent Redirect | 重定向響應 | 表示請求的資源已經被永久重定向到新的位置。 |
400 Bad Request | 客戶端錯誤響應 | 表示客戶端發送了一個無效的請求。 |
401 Unauthorized | 客戶端錯誤響應 | 表示客戶端沒有權限訪問請求的資源。 |
403 Forbidden | 客戶端錯誤響應 | 表示客戶端沒有權限訪問請求的資源。 |
404 Not Found | 客戶端錯誤響應 | 表示請求的資源不存在。 |
405 Method Not Allowed | 客戶端錯誤響應 | 表示客戶端使用的請求方法不被允許。 |
406 Not Acceptable | 客戶端錯誤響應 | 表示服務器無法根據客戶端的請求返回合適的數據。 |
407 Proxy Authentication Required | 客戶端錯誤響應 | 表示客戶端需要通過代理服務器進行身份驗證。 |
408 Request Timeout | 客戶端錯誤響應 | 表示客戶端的請求超時。 |
409 Conflict | 客戶端錯誤響應 | 表示請求的資源與服務器上的資源存在沖突。 |
410 Gone | 客戶端錯誤響應 | 表示請求的資源已經被永久刪除。 |
411 Length Required | 客戶端錯誤響應 | 表示服務器需要客戶端提供Content-Length請求頭。 |
412 Precondition Failed | 客戶端錯誤響應 | 表示客戶端的請求沒有滿足服務器的先決條件。 |
413 Payload Too Large | 客戶端錯誤響應 | 表示客戶端發送的數據太大。 |
414 URI Too Long | 客戶端錯誤響應 | 表示請求的URI太長。 |
415 Unsupported Media Type | 客戶端錯誤響應 | 表示服務器不支持客戶端發送的數據類型。 |
416 Range Not Satisfiable | 客戶端錯誤響應 | 表示客戶端請求的范圍不合法。 |
417 Expectation Failed | 客戶端錯誤響應 | 表示服務器無法滿足客戶端的Expect請求頭。 |
426 Upgrade Required | 客戶端錯誤響應 | 表示服務器需要客戶端升級到新的協議。 |
428 Precondition Required | 客戶端錯誤響應 | 表示服務器需要客戶端提供先決條件。 |
429 Too Many Requests | 客戶端錯誤響應 | 表示客戶端發送了太多的請求。 |
431 Request Header Fields Too Large | 客戶端錯誤響應 | 表示客戶端的請求頭太大。 |
451 Unavailable For Legal Reasons | 客戶端錯誤響應 | 表示請求的資源由于法律原因不可用。 |
500 Internal Server Error | 服務器錯誤響應 | 表示服務器遇到了內部錯誤。 |
501 Not Implemented | 服務器錯誤響應 | 表示服務器不支持客戶端的請求。 |
502 Bad Gateway | 服務器錯誤響應 | 表示服務器作為網關或代理服務器收到了無效的響應。 |
503 Service Unavailable | 服務器錯誤響應 | 表示服務器當前不可用。 |
504 Gateway Timeout | 服務器錯誤響應 | 表示服務器作為網關或代理服務器超時。 |
505 HTTP Version Not Supported | 服務器錯誤響應 | 表示服務器不支持客戶端使用的HTTP版本。 |
511 Network Authentication Required | 服務器錯誤響應 | 表示客戶端需要進行網絡身份驗證。 |
- 狀態碼大全:https://cloud.tencent.com/developer/chapter/13553