【JavaWeb后端開發02】SpringBootWeb + Https協議

課程內容:

  • 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端口號 。

在這里插入圖片描述

起步依賴:

image-20250419121619476

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 服務端發生不可預期的錯誤

image-20250419151032237

在這里插入圖片描述

image-20250419151517706

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>");}}

瀏覽器訪問測試:

響應狀態碼 和 響應頭如果沒有特殊要求的話,通常不手動設定。服務器會根據請求處理的邏輯,自動設置響應狀態碼和響應頭。

image-20250419154540964

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格式返回,再響應給前端}}

代碼解讀

  1. 包引用

    • 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自帶的類,表示日期和時間。

  2. @RestController這是一個Spring框架的注解,告訴Java:“這個類是用來處理網頁請求的,而且返回值會自動變成JSON格式。@RestController@Controller@ResponseBody的組合,專門用于RESTful風格的API(簡單說,就是給前端返回數據

  3. @RequestMapping("/list")

  4. 告訴Spring,當用戶訪問/list這個網頁地址時,調用下面的方法。

    解釋@RequestMapping是Spring的注解,/list是URL路徑,比如http://localhost:8080/list

  5. public List<User> list() throws Exception {

    • 作用:定義一個方法,名字叫list,返回值是一個List<User>(用戶對象的列表)。
  6. InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");

    • this.getClass():獲取當前類的信息(UserController)。

      getClass():這是 Java 中每個對象都有的方法,調用它會返回這個對象的類信息。類信息用一個 Class 對象表示。簡單說,如果 thisUserController 的實例,那么 this.getClass() 就返回 UserController.class.作用是獲取當前對象的類信息。那么,這個“類信息”具體是什么呢?簡單來說,它是一個 Class 對象,代表了當前對象所屬的類的“身份卡”或者“藍圖”。這個“身份卡”包含了關于類的所有元數據(metadata),比如類的名字、所在的包、方法、字段等。

    • getClassLoader():拿到類的“加載器”,負責找資源

      類加載器是什么? 在 Java 中,類加載器是一個幕后英雄,負責把類和資源(比如文件、圖片)從磁盤、JAR 包或其他地方加載到程序中運行。每個類都有一個類加載器,幫它完成“搬運”工作。

    • getResourceAsStream("user.txt"):從類路徑(通常是src/main/resources目錄)加載user.txt文件,返回一個輸入流(InputStream)。

      getResourceAsStream(String name):這是 ClassLoader 類提供的方法,告訴類加載器去類路徑(classpath)中找一個名叫 name 的資源(文件),然后返回一個可以讀取這個資源的輸入流

  7. 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>,每行是一個字符串。
  8. 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")。

    { ... } 里面是具體的轉換邏輯。

  9. 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"]
  10. Integer id = Integer.parseInt(parts[0]);

    • parts[0]"1",用Integer.parseInt把字符串"1"變成整數1
    • Integer是整數類型。
  11. 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:把字符串按格式解析成日期時間對象。
  12. return new User(id, userName, password, name, age, updateTime);

    • 用這些字段創建一個 User 對象并返回。
  13. }).toList();

    作用:把流水線上的所有User對象收集成一個List<User>

    解釋

    • map處理完每行后,流水線里是一堆User對象。
    • toList()把它們裝進一個列表。

4). 啟動服務測試,訪問:http://localhost:8080/user.html

image-20250419165352927

為什么服務器能自動轉換為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注解了。

image-20250419165719105

3.4 問題分析

上述案例的功能,我們雖然已經實現,但是呢,我們會發現案例中:解析文本文件中的數據,處理數據的邏輯代碼,給頁面響應的代碼全部都堆積在一起了,全部都寫在controller方法中了。

image-20250419165948337

在這里插入圖片描述

當前程序的這個業務邏輯還是比較簡單的,如果業務邏輯再稍微復雜一點,我們會看到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;}
}

具體的請求調用流程:

在這里插入圖片描述

三層架構的好處:

  1. 復用性強

  2. 便于維護

  3. 利用擴展

image-20250419183856989

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對象。

在這里插入圖片描述

image-20250420113627063

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的衍生注解:

image-20250420115123717

注解說明位置
@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。

image-20250420115726383

image-20250420115752605

4.4.2 組件掃描

問題:使用前面學習的四個注解聲明的bean,一定會生效嗎?

答案:不一定。(原因:bean想要生效,還需要被組件掃描)

  • 前面聲明bean的四大注解,要想生效,還需要被組件掃描注解 @ComponentScan 掃描。

  • 該注解雖然沒有顯式配置,但是實際上已經包含在了啟動類聲明注解 @SpringBootApplication 中,默認掃描的范圍是啟動類所在包及其子包。

image-20250420120055643

所以,我們在項目開發中,只需要按照如上項目結構,將項目中的所有的業務類,都放在啟動類所在包的子包中,就無需考慮組件掃描問題。

image-20250420120354194

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76572.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76572.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76572.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MATLAB 控制系統設計與仿真 - 37

范數魯棒控制器的設計 魯棒控制器的設計 根據雙端子狀態方程對象模型結構&#xff0c;控制器設計的目標是找到一個控制器K(s),它能保證閉環系統的范數限制在一個給定的小整數下&#xff0c;即 這時控制器的狀態方程為&#xff1a; 其中X與Y分別為下面兩個代數Riccati方程的解…

依賴沖突,缺失插件導致無法啟動項目 強制安裝命令(npm install --legacy-peer-deps)

小白終成大白 文章目錄 小白終成大白前言總結 前言 運維工程師說搞一個自動化打包流程 在服務器裝了hbuilder 找前端來啟動項目 我沒啟動起來 … 啟動報錯 failed to load config from D:\zhuque-uniapp\vite.config.js 16:17:31.601 error when starting dev server: 16:17:3…

數據戰略新范式:從中臺沉淀到服務覺醒,SQL2API 如何重塑數據價值鏈條?

一、數據中臺退燒&#xff1a;從 “戰略神話” 到 “現實拷問” 曾幾何時&#xff0c;數據中臺被視為企業數字化轉型的 “萬能解藥”&#xff0c;承載著統一數據資產、打破業務壁壘的厚望。然而&#xff0c;大量實踐暴露出其固有缺陷&#xff1a;某零售企業投入 500 萬元建設中…

警惕阿里云中的yum update操作不當導致:/sbin/init被清空導致Linux無法正常啟動

由于使用阿里云進行部署測試&#xff0c;因而會對yum update進行操作&#xff0c;這兩天更新了systemd-239-82.0.3.4.al8.2.x86_64&#xff0c;但存在報錯&#xff0c;然后進行yum history undo和清空yum cache&#xff0c;但出現操作Linux命令行無效。具體來說&#xff0c;幾個…

論文閱讀:2023 ICLR Safe RLHF: Safe Reinforcement Learning from Human Feedback

總目錄 大模型安全相關研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 Safe RLHF: Safe Reinforcement Learning from Human Feedback 安全 RLHF:通過人類反饋進行安全強化學習 https://arxiv.org/pdf/2310.12773 https://github.com/PKU-Alignment/safe…

android rom打包解包工具,Android ROM定制:boot.img、recovery解包打包

安卓boot.img和recovery.img解析與修改指南 安卓映像文件結構解析 大家都知道安卓的核心更換是在boot.img里面&#xff0c;那么如何在Windows下解開它呢&#xff1f;這里介紹一個實用的方法。 首先需要獲取bootimg.exe工具&#xff0c;這個工具最初是為華為設備開發的&#…

cdp-(Chrome DevTools Protocol) browserscan檢測原理逆向分析

https://www.browserscan.net/zh/bot-detection 首先,打開devtools后訪問網址,檢測結果網頁顯示紅色Robot,標簽插入位置,確定斷點位置可以hook該方法,也可以使用插件等方式找到這個位置,本篇不討論. Robot標簽是通過insertBefore插入的. 再往上追棧可以發現一個32長度數組,里面…

PostgreSQL 通過 copy 命令導入幾何數據 及 通過 CopyManager.copyIn() 導入幾何數據

COPY命令介紹 copy是postgresql提供的一個專門用于快速導入導出數據的命令,通常用于從文件(TXT、CSV等)或標準輸入輸出中讀取或寫入數據。適合批量導入導出數據,速度快。 默認情況下,如果在處理過程中遇到錯誤,COPY將失敗。 COPY只能用于表,不能用于視圖!!! COPY…

常用的幾種 Vue 父子組件傳值方式

1. 父組件向子組件傳值(props) 父組件代碼:Parent.vue <template><div><h2>父組件</h2><Child :parent-msg="parentMsg" /></div> </template><script> import Child from ./Child.vue;export default {componen…

【1】云原生,kubernetes 與 Docker 的關系

Kubernetes&#xff1f;K8s&#xff1f; Kubernetes經常被寫作K8s。其中的數字8替代了K和s中的8個字母——這一點倒是方便了發推&#xff0c;也方便了像我這樣懶惰的人。 什么是云原生&#xff1f; 云原生&#xff1a; 它是一種構建和運行應用程序的方法&#xff0c;它包含&am…

Eureka搭建

1.注冊中心server端 1.1.引入依賴 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</…

2025年最新版動漫短劇系統開發小程序app教程,源碼部署上線

以下是動漫短劇系統開發上線的詳細教程&#xff0c;包含從0到1的全流程&#xff1a; 一、需求分析&#xff08;關鍵&#xff1a;明確核心功能&#xff09; 核心功能清單&#xff1a; 用戶端&#xff1a;短視頻瀏覽、彈幕評論、收藏/點贊、創作者關注、付費訂閱。創作者端&am…

集成學習基礎應用實踐

集成學習簡介 學習目標&#xff1a; 1.知道集成學習是什么&#xff1f; 2.了解集成學習的分類 3.理解bagging集成的思想 4.理解boosting集成的思想 知道】集成學習是什么&#xff1f; 集成學習是機器學習中的一種思想&#xff0c;它通過多個模型的組合形成一個精度更高的…

系統與網絡安全------彈性交換網絡(1)

資料整理于網絡資料、書本資料、AI&#xff0c;僅供個人學習參考。 Trunk原理與配置 Trunk原理概述 Trunk&#xff08;虛擬局域網中繼技術&#xff09;是指能讓連接在不同交換機上的相同VLAN中的主機互通。 VLAN內通信 實現跨交換的同VLAN通信&#xff0c;通過Trunk鏈路&am…

python-圖片分割

圖片分割是圖像處理中的一個重要任務&#xff0c;它的目標是將圖像劃分為多個區域或者對象&#xff0c;例如分割出物體、前景背景或特定的部分。在 Python 中&#xff0c;常用的圖片分割方法包括傳統的圖像處理技術&#xff08;例如閾值分割、區域生長等&#xff09;和深度學習…

【Linux】軟件管理機制和軟件安裝

文章目錄 1. 軟件管理器1.1 Linux的軟件管理方式1.2 Linux的常見軟件管理機制RPM和dpkg 2. RPM2.1 rpm安裝2.2 rpm升級與更新2.3 rpm查詢2.4 rpm驗證與數字簽名2.5 rpm反安裝與重建數據庫 3. YUM3.1 yum查詢3.2 yum安裝/升級3.3 yum刪除3.4 yum的配置文件3.5 yum的軟件群組功能…

Android第三次面試總結(網絡篇)

在計算機網絡領域&#xff0c;網絡模型是理解通信原理的基礎框架。本文將詳細解析 OSI 參考模型和 TCP/IP 模型的分層結構、核心功能及實際應用&#xff0c;并通過對比幫助讀者建立完整的知識體系。 一、OSI 參考模型&#xff1a;七層架構的理論基石 OSI&#xff08;開放系統…

OpenCV day5

函數內容接上文&#xff1a;OpenCV day4-CSDN博客 目錄 9.cv2.adaptiveThreshold(): 10.cv2.split()&#xff1a; 11.cv2.merge()&#xff1a; 12.cv2.add()&#xff1a; 13.cv2.subtract()&#xff1a; 14.cv2.multiply()&#xff1a; 15.cv2.divide()&#xff1a; 1…

智能運維新范式

在制造業、設備制造、工業物聯網等領域&#xff0c;“服務周期長、響應效率低” 始終是產品運維的痛點 —— 設備故障突發時&#xff0c;工程師千里奔赴現場的耗時耗力&#xff1b;非計劃停機帶來的生產損失&#xff1b;客戶對服務體驗的更高期待…… 傳統運維模式早已難以適應…

實現定時發送郵件,以及時間同步

定時發送郵件 部署郵件服務 查看有沒有安裝mailx,安裝了 [root192 ~]# rpm -q mailx mailx-12.5-43.fc38.x86_64去網易拿一下授權碼,寫到配置文件里 vim /etc/mail.rcset fromxxxxxxx163.com set smtpsmtp.163.com set smtp-auth-userxxxxxxx163.com set smtp-auth-passwor…