?
目錄
前言(SpringBoot程序請求響應(yīng)流程)
一、請求
1、Postman(接口測試工具)
①、介紹
②、安裝
2、簡單參數(shù)
①、原始方式
②、SpringBoot方法
③、小結(jié)
3、實(shí)體參數(shù)
①、簡單實(shí)體對象
②、復(fù)雜實(shí)體對象
③、小結(jié)
4、數(shù)組集合參數(shù)
①、數(shù)組
②、集合
③、小結(jié)
5、日期參數(shù)
6、JSON參數(shù)
①、Postman在發(fā)送請求時,如何傳遞json格式的請求參數(shù)
②、在服務(wù)端的controller方法中,如何接收json格式的請求參數(shù)
7、路徑參數(shù)
①、傳遞單個參數(shù)
②、傳遞多個參數(shù)
8、小結(jié)
二、響應(yīng)
1、@ResponseBody
2、統(tǒng)一響應(yīng)結(jié)果
①、定義一個統(tǒng)一響應(yīng)結(jié)構(gòu)類 Result
②、小結(jié)
3、案例
①、需求說明
②、實(shí)現(xiàn)步驟
③、代碼實(shí)現(xiàn)
④、問題分析
三、分層解耦
1、三層架構(gòu)
①、三層架構(gòu)程序的執(zhí)行流程
②、代碼拆分
③、三層架構(gòu)的好處
2、分層解耦
①、耦合問題
②、解耦思想
3、IOC & DI 入門
4、IOC詳解
①、@Component的衍生注解
②、bean的聲明
③、組件掃描
④、小結(jié)
5、DI詳解
①、@Autowired(自動裝配)
②、小結(jié)
前言(SpringBoot程序請求響應(yīng)流程)
以上一章的程序?yàn)槔?,一個基于SpringBoot的方式開發(fā)一個web應(yīng)用,瀏覽器發(fā)起請求 /hello 后 ,給瀏覽器返回字符串 “Hello World ~”。
的Tomcat)。而我們在開發(fā)web程序時呢,定義了一個控制器類Controller,請求會被部署在Tomcat中的Controller接收,然后Controller再給瀏覽器一個響應(yīng),響應(yīng)一個字符在瀏覽器發(fā)起請求,請求了我們的后端web服務(wù)器(也就是內(nèi)置串 “Hello World”。 而在請求響應(yīng)的過程中是遵循HTTP協(xié)議的。
但是在Tomcat這類Web服務(wù)器中,是不識別我們自己定義的Controller的。而Tomcat是一個Servlet容器,支持Serlvet規(guī)范,因此在tomcat中是可以識別 Servlet程序的。
那么在SpringBoot進(jìn)行web程序開發(fā)時,它其實(shí)內(nèi)置了一個核心的Servlet程序 DispatcherServlet,稱之為 核心控制器,也可以叫做 前端控制器。 DispatcherServlet 負(fù)責(zé)接收頁面發(fā)送的請求,然后根據(jù)執(zhí)行的規(guī)則,將請求再轉(zhuǎn)發(fā)給后面的請求處理器Controller,請求處理器處理完請求之后,最終再由DispatcherServlet給瀏覽器響應(yīng)數(shù)據(jù)。
那將來瀏覽器發(fā)送請求,會攜帶請求數(shù)據(jù),包括:請求行、請求頭;請求到達(dá)tomcat之后,tomcat會負(fù)責(zé)解析這些請求數(shù)據(jù),然后呢將解析后的請求數(shù)據(jù)會傳遞給Servlet程序中的HttpServletRequest對象,那也就意味著 HttpServletRequest 對象就可以獲取到請求數(shù)據(jù)。 而Tomcat,還給Servlet程序傳遞了一個參數(shù) HttpServletResponse,通過這個對象,我們就可以給瀏覽器設(shè)置響應(yīng)數(shù)據(jù) 。
一、請求
1、Postman(接口測試工具)
①、介紹
Postman是一款支持http協(xié)議的接口調(diào)試與測試工具,它不僅可以調(diào)試簡單的css、html、腳本等簡單的網(wǎng)頁基本信息,還可以發(fā)送幾乎所有類型的HTTP請求。
②、安裝
Ⅰ、運(yùn)行安裝包
Ⅱ、創(chuàng)建一個Postman的賬號
Ⅲ、創(chuàng)建一個工作空間
Ⅳ、在新創(chuàng)建的工作空間中添加一個請求
配置請求:
Ⅴ、保存測試數(shù)據(jù) (Ctrl+s)
2、簡單參數(shù)
①、原始方式
Tomcat接收到http請求時:把請求的相關(guān)信息封裝到HttpServletRequest對象中
在Controller中,我們要想獲取Request對象,可以直接在方法的形參中聲明 HttpServletRequest 對象。然后就可以通過該對象來獲取請求信息:
//根據(jù)指定的參數(shù)名獲取請求參數(shù)的數(shù)據(jù)值 String? request.getParameter("參數(shù)名") |
@RestController public class RequestController { ??? //原始方式 ??? @RequestMapping("/simpleParam") ??? public String simpleParam(HttpServletRequest request){ ??????? // http://localhost:8080/simpleParam?name=Tom&age=10 ??????? // 請求參數(shù): name=Tom&age=10?? (有2個請求參數(shù)) ??????? // 第1個請求參數(shù): name=Tom?? 參數(shù)名:name,參數(shù)值:Tom ??????? // 第2個請求參數(shù): age=10???? 參數(shù)名:age , 參數(shù)值:10 ??????? String name = request.getParameter("name");//name就是請求參數(shù)名 ??????? String ageStr = request.getParameter("age");//age就是請求參數(shù)名 ??????? int age = Integer.parseInt(ageStr);//需要手動進(jìn)行類型轉(zhuǎn)換 ??????? System.out.println(name+"? :? "+age); ??????? return "OK"; ??? } } |
該方式僅做了解接口,在以后的開發(fā)中基本不會用到,原因是因?yàn)椋?/p> 1. 寫法比較繁瑣 2. 需要手動的進(jìn)行類型轉(zhuǎn)換 |
②、SpringBoot方法
在Springboot方法中可以自動進(jìn)行類型轉(zhuǎn)換。在Springboot的環(huán)境中,對原始的API進(jìn)行了封裝,接收參數(shù)的形式更加簡單。 對于簡單參數(shù)來講,只要保證請求參數(shù)名和Controller方法中的形參名保持一致,就可以獲取到請求參數(shù)中的數(shù)據(jù)值。
發(fā)送Post請求:
如果形參與請求參數(shù)對應(yīng)不上,也可以通過注解@RequestParam來進(jìn)行映射:
但是如果沒有設(shè)置@RequestParam注解,且方法形參名與請求參數(shù)名不一致,那么雖然會無法接收到請求數(shù)據(jù),但是它不會報錯
注解@RequestParam中的required屬性默認(rèn)為true,代表該請求參數(shù)必須傳遞,如果不傳遞就會報錯:
③、小結(jié)
3、實(shí)體參數(shù)
在使用簡單參數(shù)做為數(shù)據(jù)傳遞方式時,前端傳遞了多少個請求參數(shù),后端controller方法中的形參就要書寫多少個。如果請求參數(shù)比較多,通過上述的方式一個參數(shù)一個參數(shù)的接收,會比較繁瑣。
①、簡單實(shí)體對象
此時,我們可以考慮將請求參數(shù)封裝到一個實(shí)體類對象中。 要想完成數(shù)據(jù)封裝,需要遵守如下規(guī)則:請求參數(shù)名與實(shí)體類的屬性名相同
代碼示例:
效果展示:
②、復(fù)雜實(shí)體對象
User.java:
復(fù)雜實(shí)體對象示例:
效果展示:
③、小結(jié)
如果是復(fù)雜實(shí)體對象,也只需按照對象層次結(jié)構(gòu)關(guān)系即可接收嵌套實(shí)體類屬性參數(shù)
4、數(shù)組集合參數(shù)
數(shù)組集合參數(shù)的使用場景:在HTML的表單中,有一個表單項(xiàng)是支持多選的(復(fù)選框),可以提交選擇的多個值
多個值是怎么提交的呢?其實(shí)多個值也是一個一個逐個提交的
①、數(shù)組
②、集合
后端程序接收上述多個值的方式有兩種:
|
如果要接收集合的話,需要加上@RequestParam這個注解,如果不加的話,默認(rèn)接收的是數(shù)組形式 |
③、小結(jié)
5、日期參數(shù)
因?yàn)槿掌诘母袷蕉喾N多樣(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么對于日期類型的參數(shù)在進(jìn)行封裝的時候,需要通過@DateTimeFormat注解,以及其pattern屬性來設(shè)置日期的格式
6、JSON參數(shù)
在前后端進(jìn)行交互時,如果是比較復(fù)雜的參數(shù),前后端通過會使用JSON格式的數(shù)據(jù)進(jìn)行傳輸。 (JSON是開發(fā)中最常用的前后端數(shù)據(jù)交互方式)
①、Postman在發(fā)送請求時,如何傳遞json格式的請求參數(shù)
②、在服務(wù)端的controller方法中,如何接收json格式的請求參數(shù)
服務(wù)端Controller方法接收J(rèn)SON格式數(shù)據(jù):
- 傳遞json格式的參數(shù),在Controller中會使用實(shí)體類進(jìn)行封裝。
- 封裝規(guī)則:JSON數(shù)據(jù)鍵名與形參對象屬性名相同,定義POJO類型形參即可接收參數(shù)。需要使用 @RequestBody標(biāo)識。
- @RequestBody注解:將JSON數(shù)據(jù)映射到形參的實(shí)體類對象中(JSON中的key和實(shí)體類中的屬性名保持一致)
控制臺效果:
7、路徑參數(shù)
傳統(tǒng)的開發(fā)中請求參數(shù)是放在請求體(POST請求)傳遞或跟在URL后面通過?key=value的形式傳遞(GET請求)
而在現(xiàn)在的開發(fā)中,還是經(jīng)常會直接在請求的URL中傳遞參數(shù)。例如:
http://localhost:8080/user/1???????????????? http://localhost:880/user/1/0 |
上述的這種傳遞請求參數(shù)的形式,就稱之為 路徑參數(shù)
①、傳遞單個參數(shù)
路徑參數(shù): - 前端:通過請求URL直接傳遞參數(shù) - 后端:使用{…}來標(biāo)識該路徑參數(shù),需要使用@PathVariable獲取路徑參數(shù) |
|
②、傳遞多個參數(shù)
和傳遞單個參數(shù)方法相同,無非就是多寫一個形參和注解,需要注意的是:形參要和傳遞的參數(shù)相同才能接收成功
8、小結(jié)
二、響應(yīng)
1、@ResponseBody
在我們前面所編寫的controller方法中,都已經(jīng)設(shè)置了響應(yīng)數(shù)據(jù),那controller方法中的return的結(jié)果,怎么就可以響應(yīng)給瀏覽器呢?
答案:使用 @ResponseBody 注解
@RestController是兩個注解的組合,@RestController = @Controller + @ResponseBody
2、統(tǒng)一響應(yīng)結(jié)果
大家有沒有發(fā)現(xiàn)一個問題,我們在前面所編寫的這些Controller方法中,返回值各種各樣,沒有任何的規(guī)范
如果我們開發(fā)一個大型項(xiàng)目,項(xiàng)目中controller方法將成千上萬,使用上述方式將造成整個項(xiàng)目難以維護(hù)。那在真實(shí)的項(xiàng)目開發(fā)中是什么樣子的呢?
①、定義一個統(tǒng)一響應(yīng)結(jié)構(gòu)類 Result
在真實(shí)的項(xiàng)目開發(fā)中,無論是哪種方法,我們都會定義一個統(tǒng)一的返回結(jié)果。方案如下:
> 前端:只需要按照統(tǒng)一格式的返回結(jié)果進(jìn)行解析(僅一種解析方案),就可以拿到數(shù)據(jù)。
統(tǒng)一的返回結(jié)果使用類來描述,在這個結(jié)果中包含: - 響應(yīng)狀態(tài)碼(code):當(dāng)前請求是成功,還是失敗 - 狀態(tài)碼信息(msg):給頁面的提示信息 - 返回的數(shù)據(jù)(data):給前端響應(yīng)的數(shù)據(jù)(字符串、對象、集合) |
定義在一個實(shí)體類Result來包含以上信息,代碼如下:
public class Result { ??? private Integer code;//響應(yīng)碼,1 代表成功; 0 代表失敗 ??? private String msg;? //響應(yīng)碼 描述字符串 ??? private Object data; //返回的數(shù)據(jù) ??? public Result() { } ??? public Result(Integer code, String msg, Object data) { ??????? this.code = code; ??????? this.msg = msg; ??????? this.data = data; ??? } ??? public Integer getCode() { ??????? return code; ??? } ??? public void setCode(Integer code) { ??????? this.code = code; ??? } ??? public String getMsg() { ??????? return msg; ??? } ??? public void setMsg(String msg) { ??????? this.msg = msg; ??? } ??? public Object getData() { ??????? return data; ??? } ??? public void setData(Object data) { ??????? this.data = data; ??? } ??? //增刪改 成功響應(yīng)(不需要給前端返回數(shù)據(jù)) ??? public static Result success(){ ??????? return new Result(1,"success",null); ??? } ??? //查詢 成功響應(yīng)(把查詢結(jié)果做為返回數(shù)據(jù)響應(yīng)給前端) ??? public static Result success(Object data){ ??????? return new Result(1,"success",data); ??? } ??? //失敗響應(yīng) ??? public static Result error(String msg){ ??????? return new Result(0,msg,null); ??? } } |
效果展示:
②、小結(jié)
3、案例
①、需求說明
②、實(shí)現(xiàn)步驟
Ⅰ、在pom.xml文件中引入dom4j的依賴,用于解析XML文件
<dependency> ??? <groupId>org.dom4j</groupId> ??? <artifactId>dom4j</artifactId> ??? <version>2.1.3</version> </dependency> |
Ⅱ、引入資料中提供的:解析XML的工具類XMLParserUtils、實(shí)體類Emp、XML文件emp.xml
Ⅲ、引入資料中提供的靜態(tài)頁面文件,放在resources下的static目錄下
Ⅳ、創(chuàng)建EmpController類,編寫Controller程序,處理請求,響應(yīng)數(shù)據(jù)
③、代碼實(shí)現(xiàn)
Contriller代碼: |
|
統(tǒng)一返回結(jié)果實(shí)體類 Result:同上
效果展示:
④、問題分析
上述案例的功能,我們雖然已經(jīng)實(shí)現(xiàn),但是呢,我們會發(fā)現(xiàn)案例中:解析XML數(shù)據(jù),獲取數(shù)據(jù)的代碼,處理數(shù)據(jù)的邏輯的代碼,給頁面響應(yīng)的代碼全部都堆積在一起了,全部都寫在controller方法中了
當(dāng)前程序的這個業(yè)務(wù)邏輯還是比較簡單的,如果業(yè)務(wù)邏輯再稍微復(fù)雜一點(diǎn),我們會看到Controller方法的代碼量就很大了。 - 當(dāng)我們要修改操作數(shù)據(jù)部分的代碼,需要改動Controller - 當(dāng)我們要完善邏輯處理部分的代碼,需要改動Controller - 當(dāng)我們需要修改數(shù)據(jù)響應(yīng)的代碼,還是需要改動Controller |
這樣呢,就會造成我們整個工程代碼的復(fù)用性比較差,而且代碼難以維護(hù)。 那如何解決這個問題呢?其實(shí)在現(xiàn)在的開發(fā)中,有非常成熟的解決思路,那就是分層開發(fā)。 |
三、分層解耦
在我們進(jìn)行程序設(shè)計以及程序開發(fā)時,盡可能讓每一個接口、類、方法的職責(zé)更單一些(單一職責(zé)原則) |
單一職責(zé)原則:一個類或一個方法,就只做一件事情,只管一塊功能 這樣就可以讓類、接口、方法的復(fù)雜度更低,可讀性更強(qiáng),擴(kuò)展性更好,也更利用后期的維護(hù) |
1、三層架構(gòu)
那其實(shí)我們上述案例的處理邏輯呢,從組成上看可以分為三個部分: - 數(shù)據(jù)訪問:負(fù)責(zé)業(yè)務(wù)數(shù)據(jù)的維護(hù)操作,包括增、刪、改、查等操作。 - 邏輯處理:負(fù)責(zé)業(yè)務(wù)邏輯處理的代碼。 - 請求處理、響應(yīng)數(shù)據(jù):負(fù)責(zé),接收頁面的請求,給頁面響應(yīng)數(shù)據(jù)。 |
按照上述的三個組成部分,在我們項(xiàng)目開發(fā)中呢,可以將代碼分為三層:
①、三層架構(gòu)程序的執(zhí)行流程
思考:按照三層架構(gòu)的思想,如何要對業(yè)務(wù)邏輯(Service層)進(jìn)行變更,會影響到Controller層和Dao層嗎? |
答案:不會影響。 (程序的擴(kuò)展性、維護(hù)性變得更好了) |
②、代碼拆分
我們使用三層架構(gòu)思想,來改造下之前的程序: - 控制層包名:xxxx.controller - 業(yè)務(wù)邏輯層包名:xxxx.service - 數(shù)據(jù)訪問層包名:xxxx.dao |
|
控制層:接收前端發(fā)送的請求,對請求進(jìn)行處理,并響應(yīng)數(shù)據(jù)
業(yè)務(wù)邏輯層:處理具體的業(yè)務(wù)邏輯
數(shù)據(jù)訪問層:負(fù)責(zé)數(shù)據(jù)的訪問操作,包含數(shù)據(jù)的增、刪、改、查
代碼調(diào)用邏輯圖:
③、三層架構(gòu)的好處
三層架構(gòu)的好處: 1. 復(fù)用性強(qiáng) 2. 便于維護(hù) 3. 利用擴(kuò)展 |
2、分層解耦
解耦:解除耦合
①、耦合問題
首先需要了解軟件開發(fā)涉及到的兩個概念:內(nèi)聚和耦合 - 內(nèi)聚:軟件中各個功能模塊內(nèi)部的功能聯(lián)系 - 耦合:衡量軟件中各個層/模塊之間的依賴、關(guān)聯(lián)的程度 |
軟件設(shè)計原則:高內(nèi)聚低耦合 |
高內(nèi)聚指的是:一個模塊中各個元素之間的聯(lián)系的緊密程度,如果各個元素(語句、程序段)之間的聯(lián)系程度越高,則內(nèi)聚性越高,即 "高內(nèi)聚" 低耦合指的是:軟件中各個層、模塊之間的依賴關(guān)聯(lián)程序越低越好 |
程序中高內(nèi)聚的體現(xiàn): |
- EmpServiceA類中只編寫了和員工相關(guān)的邏輯處理代碼 |
程序中耦合代碼的體現(xiàn): |
- 把業(yè)務(wù)類變?yōu)镋mpServiceB時,需要修改controller層中的代碼 |
高內(nèi)聚、低耦合的目的是使程序模塊的可重用性、移植性大大增強(qiáng)
②、解耦思想
現(xiàn)在寫的代碼存在耦合 |
那我們應(yīng)該如何解耦呢?首先第一步不能在EmpController中使用new對象,把new這一塊代碼刪掉 |
此時,就存在另一個問題了,不能new,就意味著沒有業(yè)務(wù)層對象(程序運(yùn)行就報錯),怎么辦呢? 我們的解決思路是: - 提供一個容器,容器中存儲一些對象(例:EmpService對象) - controller程序從容器中獲取EmpService類型的對象 |
我們想要實(shí)現(xiàn)上述解耦操作,就涉及到Spring中的兩個核心概念:
? > 對象的創(chuàng)建權(quán)由程序員主動創(chuàng)建轉(zhuǎn)移到容器(由容器創(chuàng)建、管理對象)。這個容器稱為:IOC容器或Spring容器
? > 程序運(yùn)行時需要某個資源,此時容器就為其提供這個資源。 ? > 例:EmpController程序運(yùn)行時需要EmpService對象,Spring容器就為其提供并注入EmpService對象 IOC容器中創(chuàng)建、管理的對象,稱之為:bean對象 |
3、IOC & DI 入門
思路: 1. 刪除Controller層、Service層中new對象的代碼 2. Service層及Dao層的實(shí)現(xiàn)類,交給IOC容器管理 3. 為Controller及Service注入運(yùn)行時依賴的對象 ?? - Controller程序中注入依賴的Service層對象 ?? - Service程序中注入依賴的Dao層對象 |
第1步:刪除Controller層、Service層中new對象的代碼 第2步:Service層及Dao層的實(shí)現(xiàn)類,交給IOC容器管理
第3步:為Controller及Service注入運(yùn)行時依賴的對象
|
運(yùn)行測試:
思考:通過 IOC & DI 可以完成層與層之間的代碼解耦嗎? |
答案:可以,想要切換相應(yīng)的Service 只需要打開并注釋掉其它Service的@Component注解即可 |
4、IOC詳解
IOC控制反轉(zhuǎn),就是將對象的控制權(quán)交給Spring的IOC容器,由IOC容器創(chuàng)建及管理對象。IOC容器創(chuàng)建的對象稱為bean對象。
在之前的入門案例中,要把某個對象交給IOC容器管理,需要在類上添加一個注解:@Component
①、@Component的衍生注解
而Spring框架為了更好的標(biāo)識web應(yīng)用程序開發(fā)當(dāng)中,bean對象到底歸屬于哪一層,又提供了@Component的衍生注解:
@Service與@Repository注解源碼:
②、bean的聲明
在IOC容器中,每一個Bean都有一個屬于自己的名字,可以通過注解的value屬性指定bean的名字。如果沒有指定,默認(rèn)為類名首字母小寫:
指定bean的名字(一般不用指定)
③、組件掃描
問題:使用前面學(xué)習(xí)的四個注解聲明的bean,一定會生效嗎? |
答案:不一定。(原因:bean想要生效,還需要被組件掃描) |
|
要想解決該問題,我們有兩種做法:
|
④、小結(jié)
5、DI詳解
依賴注入,是指IOC容器要為應(yīng)用程序去提供運(yùn)行時所依賴的資源,而資源指的就是對象
①、@Autowired(自動裝配)
在入門程序案例中,我們使用了@Autowired這個注解,完成了依賴注入的操作,而這個Autowired翻譯過來叫:自動裝配。
@Autowired注解,默認(rèn)是按照類型進(jìn)行自動裝配的(去IOC容器中找某個類型的對象,然后完成注入操作)
問題:如果在IOC容器中,存在多個相同類型的bean對象,會出現(xiàn)什么情況呢? |
答案:程序運(yùn)行會報錯,會提示我們不能完成自動裝配了 |
解決方案:
|
面試題 : @Autowird 與 @Resource的區(qū)別: - @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解 - @Autowired 默認(rèn)是按照類型注入,而@Resource是按照名稱注入文章來源:http://www.zghlxwxcb.cn/news/detail-670770.html |
②、小結(jié)
文章來源地址http://www.zghlxwxcb.cn/news/detail-670770.html
到了這里,關(guān)于【Java Web】005 -- 請求響應(yīng) & 分層解耦(Postman、三層架構(gòu)、IOC、DI、注解)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!