目錄
前言
一、SpringBoot入門
1.入門程序
2.解析
二、HTTP協議
1.HTTP概述
2.HTTP請求協議
2.1 GET方式的請求協議
2.2 POST方式的請求協議
2.3 兩者的區別
2.4 獲取請求數據
3.HTTP響應協議
三、分層解耦
1.三層架構
2.IOC&DI
2.1 入門
2.2 IOC詳解
2.3 DI詳解
2.4 注意事項
前言
我們可以通過HTML、CSS、JS這三項技術進行前端頁面的開發,最終,這些前端頁面,我們就可以部署在服務器上,然后打開瀏覽器就可以直接訪問服務器上部署的前端頁面了。而像這HTML、CSS、JS 以及圖片、音頻、視頻等這些資源,我們都稱為靜態資源。 所謂靜態資源,就是指在服務器上存儲的不會改變的數據,通常不會根據用戶的請求而變化。
與靜態資源相對的就是動態資源,即在服務器端進行存儲,會根據用戶請求和其他數據動態生成,內容可能會在每次請求時都發生變化。我們可以基于Spring框架來構建動態資源。
對于java程序開發的動態資源來說,我們通常會將這些動態資源部署在Tomcat(Tomcat服務器),這樣的Web服務器中運行。 而瀏覽器與服務器在通信的時候,基本都是基于HTTP協議的。
一、SpringBoot入門
1.入門程序
在com.example.demo這個包下創建HelloController類
類中的代碼如下:
package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController// 表明是個請求處理類
public class HelloController {@RequestMapping("/hello") // 標識請求路徑public String hello(String name) {System.out.println("HelloController...hello:" + name);return "Hello" + name;}
}
上面的hello方法中參數是前端給服務器提交的參數,return表示服務器給前端返回的數據。
運行SpringBoot自動生成的引導類 (標識有@SpringBootApplication
注解的類)
打開瀏覽器,輸入http://localhost:8080/hello?name=xiaoming
可以發現,前端頁面在網址欄向服務器傳遞了xiaoming這個參數,然后服務器將這個參數重新相應給了前端頁面。
2.解析
我們發現運行SpringBoot自動生成的引導類就可以啟動整個服務,這是為什么呢?
因為我們在創建springboot項目的時候,選擇了web開發的起步依賴 spring-boot-starter-web
。而spring-boot-starter-web
依賴,又依賴了spring-boot-starter-tomcat
,由于maven的依賴傳遞特性,那么在我們創建的springboot項目中也就已經有了tomcat的依賴,這個其實就springboot中內嵌的tomcat。
而運行引導類中的main方法,其實啟動的就是springboot中內嵌的Tomcat服務器。 而我們所開發的項目,也會自動的部署在該tomcat服務器中,并占用8080端口號 。
二、HTTP協議
1.HTTP概述
HTTP:Hyper Text Transfer Protocol(超文本傳輸協議),規定了瀏覽器與服務器之間數據傳輸的規則。
-
http是互聯網上應用最為廣泛的一種網絡協議
-
http協議要求:瀏覽器在向服務器發送請求數據時,或是服務器在向瀏覽器發送響應數據時,都必須按照固定的格式進行數據傳遞
HTTP協議
-
基于TCP協議: 面向連接,安全。
-
基于請求-響應模型: 一次請求對應一次響應(先請求后響應)。
-
HTTP協議是無狀態協議: 對于數據沒有記憶能力。每次請求-響應都是獨立的
????????無狀態指的是客戶端發送HTTP請求給服務端之后,服務端根據請求響應數據,響應完后,不會記錄任何信息。
2.HTTP請求協議
瀏覽器將數據以請求格式發送到服務器。包括:請求行、請求頭 、請求體。
2.1 GET方式的請求協議
-
請求行(以上圖中紅色部分) :HTTP請求中的第一行數據。由:
請求方式
、資源路徑
、協議/版本
組成(之間使用空格分隔)-
請求方式:GET
-
資源路徑:/brand/findAll?name=OPPO&status=1
-
請求路徑:/brand/findAll
-
請求參數:name=OPPO&status=1
-
請求參數是以key=value形式出現
-
多個請求參數之間使用
&
連接
-
-
請求路徑和請求參數之間使用
?
連接
-
-
協議/版本:HTTP/1.1
-
-
請求頭(以上圖中黃色部分) :第二行開始,上圖黃色部分內容就是請求頭。格式為key: value形式
-
http是個無狀態的協議,所以在請求頭設置瀏覽器的一些自身信息和想要響應的形式。這樣服務器在收到信息后,就可以知道是誰,想干什么了
-
服務端可以根據請求頭中的內容來獲取客戶端的相關信息,有了這些信息服務端就可以處理不同的業務需求。
2.2 POST方式的請求協議
-
請求行(以上圖中紅色部分):包含請求方式、資源路徑、協議/版本
-
請求方式:POST
-
資源路徑:/brand
-
協議/版本:HTTP/1.1
-
-
請求頭(以上圖中黃色部分)
-
請求體(以上圖中綠色部分) :存儲請求參數
-
請求體和請求頭之間是有一個空行隔開(作用:用于標記請求頭結束)
-
2.3 兩者的區別
2.4 獲取請求數據
web服務器對HTTP請求進行解析,并將其數據封裝到了HttpServletRequest類里面,并在調用Controller方法的時候傳遞給了該方法。
用HttpServletRequest類型 聲明一個形參(例如request),用request的方法操作具體數據。
@RestController
public class RequestController {/*** 請求路徑 http://localhost:8080/request?name=Tom&age=18* @param request* @return*/@RequestMapping("/request")public String request(HttpServletRequest request){//1.獲取請求參數 name, ageString name = request.getParameter("name"); // 請求數據以鍵值對形式存在String age = request.getParameter("age");System.out.println("name = " + name + ", age = " + age);//2.獲取請求路徑String uri = request.getRequestURI();String url = request.getRequestURL().toString();System.out.println("uri = " + uri);System.out.println("url = " + url);//3.獲取請求方式String method = request.getMethod();System.out.println("method = " + method);//4.獲取請求頭String header = request.getHeader("User-Agent");System.out.println("header = " + header);return "request success";}}
3.HTTP響應協議
服務器將數據以響應格式返回給瀏覽器。包括:響應行 、響應頭 、響應體
-
響應行(以上圖中紅色部分):響應數據的第一行。響應行由
協議及版本
、響應狀態碼
、狀態碼描述
組成-
協議/版本:HTTP/1.1
-
響應狀態碼:200
-
狀態碼描述:OK
-
-
響應頭(以上圖中黃色部分):響應數據的第二行開始。格式為key:value形式
-
http是個無狀態的協議,所以可以在請求頭和響應頭中設置一些信息和想要執行的動作,這樣,對方在收到信息后,就可以知道你是誰,你想干什么
-
-
響應體(以上圖中綠色部分): 響應數據的最后一部分。存儲響應的數據
-
響應體和響應頭之間有一個空行隔開(作用:用于標記響應頭結束)
-
關于響應狀態碼,我們主要有以下三個狀態碼:
-
200 ok
客戶端請求成功 -
404 Not Found
請求資源不存在 -
500 Internal Server Error
服務端發生不可預期的錯誤
三、分層解耦
1.三層架構
我們進行程序設計以及程序開發時,盡可能讓每一個接口、類、方法的職責更單一些(單一職責原則)。盡量滿足高內聚、低耦合的要求。
我們在項目開發中,可以把代碼分為三層,如圖所示:
-
Controller:控制層。接收前端發送的請求,對請求進行處理,并響應數據。
-
Service:業務邏輯層。處理具體的業務邏輯。
-
Dao:數據訪問層(Data Access Object),也稱為持久層。負責數據訪問操作,包括數據的增、刪、改、查。這一層主要是進行數據庫的處理。
基于三層架構執行流程如圖所示:
-
前端發起的請求,由Controller層接收(Controller響應數據給前端)
-
Controller層調用Service層來進行邏輯處理(Service層處理完后,把處理結果返回給Controller層)
-
Serivce層調用Dao層(邏輯處理過程中需要用到的一些數據要從Dao層獲取)
-
Dao層操作文件中的數據(Dao拿到的數據會返回給Service層)
2.IOC&DI
-
控制反轉: Inversion Of Control,簡稱IOC。對象的創建控制權由程序自身轉移到外部(容器),這種思想稱為控制反轉。對象的創建權由程序員主動創建轉移到容器(由容器創建、管理對象)。這個容器稱為:IOC容器或Spring容器。
-
依賴注入: Dependency Injection,簡稱DI。容器為應用程序提供運行時,所依賴的資源,稱之為依賴注入。程序運行時需要某個資源,此時容器就為其提供這個資源。
-
?
2.1 入門
在實現類加上 @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 {@Autowiredprivate 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;}
}
發現UserServiceImpl這個類中聲明了接口UserDao,但是它并沒有創建接口的實現類對象,就是因為實現類UserDaoImpl加了@Component
注解,把當前類產生的對象交給了IOC容器管理。而在UserServiceImpl這個類中聲明的接口UserDao上有@Autowired注解,它可以自動從IOC容器中取出實現類的相關對象。
2.2 IOC詳解
IOC控制反轉,就是將對象的控制權交給Spring的IOC容器,由IOC容器創建及管理對象。IOC容器創建的對象稱為bean對象。
而Spring框架為了更好的標識web應用程序開發當中,bean對象到底歸屬于哪一層,又提供了@Component的衍生注解:
注意1:聲明bean的時候,可以通過注解的value屬性指定bean的名字,如果沒有指定,默認為類名首字母小寫。
注意2:使用以上四個注解都可以聲明bean,但是在springboot集成web開發中,聲明控制器bean只能用@Controller。
2.3 DI詳解
依賴注入,是指IOC容器要為應用程序去提供運行時所依賴的資源,而資源指的就是對象。
在入門代碼中,我們使用了@Autowired這個注解,完成了依賴注入的操作,而這個Autowired翻譯過來叫:自動裝配。@Autowired
注解,默認是按照類型進行自動裝配的(去IOC容器中找某個類型的對象,然后完成注入操作)
@Autowired 進行依賴注入,常見的方式,有如下三種:
a) 屬性注入
@RestController
public class UserController {//方式一: 屬性注入@Autowiredprivate UserService userService;}
b). 構造函數注入
@RestController
public class UserController {//方式二: 構造器注入private final UserService userService;@Autowired //如果當前類中只存在一個構造函數, @Autowired可以省略public UserController(UserService userService) {this.userService = userService;}}
3). setter注入
/*** 用戶信息Controller*/
@RestController
public class UserController {//方式三: setter注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}}
第一種是最常用的,因為代碼最為簡潔。
2.4 注意事項
那如果在IOC容器中,存在多個相同類型的bean對象,會出現什么情況呢?
在下面的例子中,準備了兩個UserService的實現類,并且都交給了IOC容器管理。 代碼如下:
啟動項目之后,控制臺報錯了
出現錯誤的原因是因為在Spring的容器中,UserService這個類型的bean存在兩個,框架不知道具體要注入哪個bean使用,所以就報錯了。
對于上述問題,有以下解決方案:
方案一:使用@Primary注解
當存在多個相同類型的Bean注入時,加上@Primary注解,來確定默認的實現。
@Primary
@Service
public class UserServiceImpl implements UserService {
}
方案二:使用@Qualifier注解
指定當前要注入的bean對象。 在@Qualifier的value屬性中,指定注入的bean的名稱。 @Qualifier注解不能單獨使用,必須配合@Autowired使用。
@RestController
public class UserController {@Qualifier("userServiceImpl")@Autowiredprivate UserService userService;
}
方案三:使用@Resource注解
@RestController
public class UserController {@Resource(name = "userServiceImpl")private UserService userService;
}
@Autowird 與 @Resource有什么區別?
-
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
-
@Autowired 默認是按照類型注入,而@Resource是按照名稱注入