詳解SpringBoot中如何使用Reactor模型
Spring Boot使用的Reactor模型是一種基于Java的反應(yīng)式編程框架,屬于Spring WebFlux框架的核心部分。Reactor模型主要提供了一種在Java虛擬機上構(gòu)建非阻塞應(yīng)用的方式,這種方式使用了響應(yīng)式編程原理,通過響應(yīng)式流(Reactive Streams)標準來實現(xiàn)。
簡單介紹
基本概念
響應(yīng)式編程(Reactive Programming): 響應(yīng)式編程是一種異步編程范式,關(guān)注于數(shù)據(jù)流和變化的傳播。這意味著可以在數(shù)據(jù)發(fā)生變化時自動將變化傳遞給程序的其他部分。
響應(yīng)式流(Reactive Streams): 為各種編程語言提供了一套共通的API,目的是以異步的方式處理數(shù)據(jù)流,并能夠以背壓(Backpressure)的形式控制資源消耗。背壓是一種防止消費者處理速度跟不上生產(chǎn)者產(chǎn)生速度的機制。
Reactor模型的組件
Reactor模型主要包括兩個基本的構(gòu)件:
Mono:代表一個異步的計算結(jié)果,它最終會返回一個值或者一個錯誤信號。Mono是0-1的概念,要么是一個值,要么是一個完成信號,要么是一個錯誤信號。
Flux:代表一個異步的序列,它可以發(fā)出多個值。Flux是0-N的概念,可以發(fā)出零個、一個或者多個值,還可以發(fā)出完成或錯誤信號。
優(yōu)勢與原理
Spring WebFlux和Reactor
Spring WebFlux是Spring框架對反應(yīng)式編程的支持,它內(nèi)部大量使用了Reactor模型。WebFlux使用Reactor來處理HTTP請求,每一個請求都被封裝成一個Flux或Mono,Spring框架負責管理這些請求的生命周期,從而實現(xiàn)非阻塞和高效的請求處理。通過使用Reactor模型和Spring WebFlux,開發(fā)者可以創(chuàng)建出既能夠高效處理大量并發(fā)請求,也能夠保持較低資源消耗的應(yīng)用程序,這在現(xiàn)代的微服務(wù)架構(gòu)中非常有價值。Spring Boot中的Reactor模型通過提供一種基于事件驅(qū)動和非阻塞的應(yīng)用開發(fā)方式,使得能夠構(gòu)建高性能且易于擴展的微服務(wù)。在這個模型中,Spring WebFlux是主要的執(zhí)行者,而Reactor則是其反應(yīng)式編程核心。接下來,我們會詳細探討Reactor模型的優(yōu)勢和原理。
優(yōu)勢
非阻塞I/O操作:
Reactor模型使用非阻塞I/O,這意味著線程不會因為I/O操作(如讀取文件或網(wǎng)絡(luò)通信)而被掛起。這可以顯著減少對線程的需求,從而降低系統(tǒng)的資源消耗,提高系統(tǒng)的響應(yīng)速度和吞吐量。
高效的資源使用:
傳統(tǒng)的阻塞I/O模型中,每個連接通常需要一個線程,線程數(shù)的增加會導致內(nèi)存消耗的增加和上下文切換的開銷。而在非阻塞模型中,可以用很少的線程處理大量的連接,極大地提升了資源利用效率。
支持背壓:
Reactor實現(xiàn)了響應(yīng)式流規(guī)范中的背壓機制,這允許消費者按其處理能力從生產(chǎn)者處接收數(shù)據(jù),避免了內(nèi)存溢出和處理瓶頸的問題。
靈活的錯誤處理:
在響應(yīng)式流中,錯誤處理可以被嵌入到數(shù)據(jù)流的處理過程中,允許開發(fā)者控制錯誤恢復策略,如重新嘗試操作或回退。
響應(yīng)式編程的簡化:
Reactor提供了豐富的操作符來處理異步數(shù)據(jù)流,這簡化了響應(yīng)式編程模型的使用,使得開發(fā)者可以更容易地實現(xiàn)復雜的數(shù)據(jù)流轉(zhuǎn)換和組合邏輯。
原理
響應(yīng)式流規(guī)范
響應(yīng)式流規(guī)范(Reactive Streams)是一種為了處理異步數(shù)據(jù)流而制定的標準,它定義了在JVM(Java虛擬機)上進行非阻塞背壓(backpressure)的流處理的標準。在Spring Boot和其他現(xiàn)代Java應(yīng)用中,響應(yīng)式流的概念是至關(guān)重要的,尤其是在使用Reactor框架時。以下是響應(yīng)式流規(guī)范中定義的四個主要接口的詳細解析:
1. Publisher
Publisher
是一個接口,它代表一個數(shù)據(jù)序列的生產(chǎn)者。在響應(yīng)式編程中,它是數(shù)據(jù)流的源頭,負責發(fā)布數(shù)據(jù)項給它的訂閱者(Subscriber)。這里的數(shù)據(jù)項可以是任何類型的對象。
- 功能:
Publisher
可以發(fā)布無限個數(shù)據(jù)項給訂閱者,它的主要方法是subscribe(Subscriber<? super T> s)
,這個方法用來注冊一個Subscriber,Publisher在有數(shù)據(jù)可提供時將數(shù)據(jù)推送給這些Subscriber。 - 用例:在一個網(wǎng)絡(luò)API調(diào)用場景中,返回的數(shù)據(jù)可以通過
Flux
或Mono
實現(xiàn)的Publisher來發(fā)布,其中每個數(shù)據(jù)項代表一個返回的數(shù)據(jù)對象。
2. Subscriber
Subscriber
是數(shù)據(jù)的消費者。一個Subscriber會訂閱一個Publisher,并通過實現(xiàn)幾個回調(diào)方法來接收和處理數(shù)據(jù)。
主要方法:
onSubscribe(Subscription s)
:當Subscriber訂閱Publisher成功時,這個方法會被調(diào)用,它接收一個Subscription
對象,該對象控制數(shù)據(jù)的流向。onNext(T t)
:每當接收到一個數(shù)據(jù)項時,這個方法被調(diào)用。onError(Throwable t)
:如果在處理數(shù)據(jù)流的過程中發(fā)生錯誤,這個方法被調(diào)用。onComplete()
:當所有數(shù)據(jù)被成功處理后,這個方法被調(diào)用,表示數(shù)據(jù)流已經(jīng)結(jié)束。
3. Subscription
Subscription
是連接Publisher和Subscriber的紐帶,它允許Subscriber管理數(shù)據(jù)流并進行背壓控制。
- 背壓控制:通過
request(long n)
方法,Subscriber可以告訴Publisher它準備好接收n個數(shù)據(jù)項。這是一種流量控制機制,可以防止Subscriber被過快的數(shù)據(jù)流淹沒。 - 取消訂閱:通過
cancel()
方法,Subscriber可以隨時停止接收數(shù)據(jù)項。
4. Processor
Processor
繼承自Publisher
和Subscriber
,它是一個中間件,可以同時接收和發(fā)布數(shù)據(jù)。
- 功能:
Processor
可以對流經(jīng)的數(shù)據(jù)進行處理和轉(zhuǎn)換,然后再發(fā)布出去。它可以用于數(shù)據(jù)過濾、轉(zhuǎn)換或者合并等操作。 - 示例:一個
Processor
可能會接收原始的股市數(shù)據(jù)流,抽取出關(guān)于特定股票的數(shù)據(jù),應(yīng)用一些算法來分析趨勢,然后發(fā)布這些信息給其他Subscriber。
響應(yīng)式流的這種設(shè)計可以幫助開發(fā)者有效地控制數(shù)據(jù)流中的背壓問題,并使異步數(shù)據(jù)流處理變得更加靈活和強大。在Reactor和其他響應(yīng)式編程庫中,這一模型被廣泛應(yīng)用于高性能的異步系統(tǒng)中,允許系統(tǒng)更加高效地利用資源,同時處理大量數(shù)據(jù)。
Mono與Flux
Mono
和Flux
是Project Reactor框架中兩個核心的反應(yīng)式編程類型,它們都是實現(xiàn)了Publisher
接口。這兩種類型用于處理不同數(shù)量的數(shù)據(jù)流,并在Spring WebFlux等環(huán)境中廣泛使用以支持異步和非阻塞的數(shù)據(jù)操作。
Mono
Mono
是一個簡化的響應(yīng)式類型,用于表示一個異步計算的結(jié)果可以是零個或一個元素。它是專為處理那些最多只返回單個值的操作或事件而設(shè)計的。
特點和用例
用例:Mono
非常適合用于單個對象的異步請求,比如請求一個網(wǎng)絡(luò)資源或者數(shù)據(jù)庫條目。例如,你可以使用Mono
來處理一個HTTP GET請求,該請求查詢并返回一個用戶對象。
操作符:Mono
支持多種操作符,例如map
(映射)、filter
(過濾)、flatMap
(扁平化映射)、和defaultIfEmpty
(如果為空則提供默認值)等,這些操作符可以用來在響應(yīng)式流中處理和轉(zhuǎn)換數(shù)據(jù)。
例子:
Mono<String> mono = Mono.just("Hello World"); // 創(chuàng)建一個包含單個元素的Mono Mono<String> newMono = mono.map(value -> value + " Reactor"); // 映射操作
Flux
Flux
是另一個核心的響應(yīng)式類型,用于表示一個包含零到多個元素的異步序列。它可以發(fā)出多個數(shù)據(jù)項,適合處理數(shù)據(jù)流。
特點和用例
- 用例:
Flux
用于需要返回多個數(shù)據(jù)項的場景,如數(shù)據(jù)庫查詢結(jié)果或者批量的網(wǎng)絡(luò)調(diào)用。一個常見的例子是從數(shù)據(jù)庫檢索所有用戶的信息,這可能返回多個用戶對象。 - 操作符:
Flux
同樣支持各種操作符來對數(shù)據(jù)流進行操作,比如concatMap
、collectList
、merge
和zip
等,這些操作符可以幫助開發(fā)者在處理數(shù)據(jù)流時實現(xiàn)更復雜的邏輯。
例子:
Flux<Integer> flux = Flux.range(1, 5); // 創(chuàng)建一個包含1到5的Flux Flux<Integer> filteredFlux = flux.filter(number -> number % 2 == 0); // 過濾操作,僅保留偶數(shù)
異同點
雖然Mono
和Flux
都可以用來處理數(shù)據(jù)流,但它們之間還是有一些重要的區(qū)別:
數(shù)量差異:
Mono
用于0或1個結(jié)果,對應(yīng)于單個值的異步操作。Flux
用于處理一個長序列的結(jié)果,可以是0到N個值。
使用場景:
- 如果你期待或允許方法返回多個值(或者沒有值),應(yīng)該使用
Flux
。 - 如果方法返回一個值或可能根本不返回值(例如空),則應(yīng)該使用
Mono
。
在實際開發(fā)中,選擇Mono
還是Flux
取決于你的具體需求——是否需要處理多個數(shù)據(jù)項,以及你的數(shù)據(jù)處理邏輯。使用正確的類型可以讓代碼更加清晰,并且能夠更好地利用Reactor提供的豐富的響應(yīng)式操作符。
調(diào)度器
在Reactor框架中,調(diào)度器(Schedulers)扮演著非常關(guān)鍵的角色,它們負責管理和控制執(zhí)行上下文,即在哪里和如何執(zhí)行響應(yīng)式流的操作。調(diào)度器使得開發(fā)者能夠精細地控制執(zhí)行環(huán)境,可以在不同的線程、線程池中執(zhí)行操作,從而實現(xiàn)更高效的資源使用和更好的應(yīng)用性能。
調(diào)度器的基本概念
調(diào)度器基本上是決定響應(yīng)式鏈中各個操作執(zhí)行的地點(即線程)的機制。在Reactor中,Scheduler
是一個接口,它封裝了線程管理和調(diào)度執(zhí)行的邏輯。使用不同的調(diào)度器實現(xiàn),可以使數(shù)據(jù)流的操作在不同的線程環(huán)境中執(zhí)行。
Reactor中常見的調(diào)度器
Reactor提供了幾種預(yù)定義的調(diào)度器,每種調(diào)度器都有其特定的用途:
immediate():
- 這是默認的調(diào)度器,它會在當前線程立即執(zhí)行所有任務(wù)。如果你不指定調(diào)度器,就會使用這個執(zhí)行。
- 使用場景:適用于簡單的任務(wù)或測試環(huán)境,不需要異步執(zhí)行。
single():
- 使用一個單一的可重用的線程來執(zhí)行所有任務(wù)。
- 使用場景:適用于不需要并行執(zhí)行且任務(wù)量不大的場合。
boundedElastic():
- 提供一個彈性的線程池,適用于I/O操作(阻塞性任務(wù))。這個調(diào)度器會根據(jù)需要創(chuàng)建新的線程,并在不使用時釋放線程。
- 使用場景:適用于執(zhí)行阻塞I/O操作,如文件讀寫、數(shù)據(jù)庫操作等。
parallel():
- 使用固定大小的線程池,適合并行任務(wù)的處理。
- 使用場景:適用于并行處理計算密集型任務(wù),如圖像或視頻處理。
elastic():
- 提供一個按需創(chuàng)建線程的調(diào)度器,這個調(diào)度器在Reactor 3.4版本中被標記為廢棄,被
boundedElastic()
替代。 - 使用場景:主要用于延遲任務(wù)或非頻繁的任務(wù)執(zhí)行。
使用調(diào)度器的示例
假設(shè)你需要從數(shù)據(jù)庫加載大量數(shù)據(jù),并進行處理,這些操作可能會阻塞線程。為了不阻塞主線程,可以使用boundedElastic()
調(diào)度器:
Flux.just("query1", "query2", "query3") .flatMap(query -> Mono.fromCallable(() -> executeQuery(query)) .subscribeOn(Schedulers.boundedElastic())) .subscribe(result -> System.out.println("Result: " + result));
在這個示例中,每個查詢都在一個可伸縮的線程池中異步執(zhí)行,這避免了主線程的阻塞,可以提高系統(tǒng)的響應(yīng)性和吞吐率。
調(diào)度器的重要性
在現(xiàn)代應(yīng)用程序,尤其是微服務(wù)和云基礎(chǔ)設(shè)施中,正確使用調(diào)度器非常關(guān)鍵。
- 控制資源使用,優(yōu)化應(yīng)用的性能。
- 提高應(yīng)用的響應(yīng)性,通過異步執(zhí)行降低延遲。
- 管理線程使用,避免常見的多線程問題,如競態(tài)條件、死鎖等。
通過調(diào)度器,Reactor給開發(fā)者提供了一個強大的工具,可以在構(gòu)建高性能、高并發(fā)的反應(yīng)式應(yīng)用時,獲得更好的控制和更優(yōu)的資源管理。
非阻塞與事件循環(huán)
在現(xiàn)代的編程模型中,非阻塞操作和事件循環(huán)機制成為構(gòu)建高性能、高可用性應(yīng)用程序的重要策略之一。Reactor框架采用了類似于Node.js的事件循環(huán)模型,來優(yōu)化異步操作和提高應(yīng)用的響應(yīng)性。以下是對Reactor中的非阻塞與事件循環(huán)模型的詳細解析。
事件循環(huán)模型的基本概念
事件循環(huán)模型是一個程序結(jié)構(gòu),用于等待和發(fā)送消息和事件。在一個簡單的事件循環(huán)模型中,有一個主循環(huán)(event loop),負責監(jiān)聽各種事件的發(fā)生并對這些事件作出反應(yīng)。這個模型的核心思想是使用單個線程(event loop線程)來處理所有事件和消息,從而避免了多線程環(huán)境中的許多復雜性,如線程同步問題。
非阻塞I/O操作
非阻塞I/O是事件循環(huán)模型能夠高效運行的關(guān)鍵。在傳統(tǒng)的阻塞I/O模型中,如果I/O操作未立即完成,執(zhí)行該操作的線程將被掛起,直到I/O操作完成。這種模式在多用戶或高并發(fā)環(huán)境中效率極低。
相反,非阻塞I/O允許系統(tǒng)在操作尚未完成時立即返回,不會掛起執(zhí)行操作的線程。這意味著同一個線程可以在等待一個I/O操作完成的同時開始執(zhí)行其他任務(wù)。
Reactor中的事件循環(huán)
在Reactor模型中,事件循環(huán)負責調(diào)度和處理所有非阻塞操作,如下所述:
單線程事件循環(huán):
Reactor使用一個單獨的線程來運行事件循環(huán)。在這個循環(huán)中,所有任務(wù)(事件)都在同一個線程中被調(diào)度和處理,這樣可以避免多線程程序常見的競態(tài)條件和鎖問題。
任務(wù)調(diào)度:
事件循環(huán)持續(xù)檢查是否有新的事件或消息需要處理。當一個非阻塞I/O操作開始時,它被放入事件隊列。一旦I/O操作完成,相關(guān)的回調(diào)函數(shù)或任務(wù)將被觸發(fā)并執(zhí)行。
利用非阻塞I/O:
所有的I/O操作都是非阻塞的,這意味著事件循環(huán)永遠不會因為等待I/O操作而停止。這種方式允許Reactor在處理大量并發(fā)請求時保持高效和響應(yīng)性。
示例
以下是一個簡化的示例,說明如何在Reactor中使用事件循環(huán)處理異步任務(wù):
Flux.range(1, 10) .publishOn(Schedulers.single()) // 使用單線程調(diào)度器 .doOnNext(i -> { System.out.println("Processed " + i + " on thread " + Thread.currentThread().getName()); // 這里可以進行數(shù)據(jù)處理,非阻塞操作 }) .blockLast(); // 等待所有事件處理完成
在這個示例中,publishOn(Schedulers.single())
確保所有處理都在單個線程上異步進行,模擬事件循環(huán)的行為。
優(yōu)勢
使用事件循環(huán)和非阻塞I/O的主要優(yōu)勢包括:
- 高效性:單線程處理所有事件,減少了線程創(chuàng)建和銷毀的開銷,同時避免了多線程的同步問題。
- 響應(yīng)性:應(yīng)用可以快速響應(yīng)事件,因為它不會在任何操作上阻塞。
- 可擴展性:可以處理大量的并發(fā)連接和事件,不受阻塞I/O的限制,特別適合于高負載環(huán)境。
簡單案例
在Spring Boot中使用Reactor的一個常見場景是構(gòu)建RESTful API,這些API能夠異步處理數(shù)據(jù)并以非阻塞的方式返回結(jié)果。這種模式非常適合處理I/O密集型任務(wù),如數(shù)據(jù)庫操作或遠程服務(wù)調(diào)用,能顯著提高應(yīng)用的響應(yīng)性和吞吐量。下面我將提供一個使用Spring WebFlux(利用Reactor框架)來實現(xiàn)的簡單REST API的例子。
場景描述
假設(shè)我們需要開發(fā)一個API,用于異步獲取用戶信息。這個API會從數(shù)據(jù)庫中查詢用戶信息,并返回給客戶端。為了簡化示例,我們將使用一個模擬的用戶數(shù)據(jù)查詢函數(shù)。
開發(fā)環(huán)境準備
首先,確保你的工程已經(jīng)添加了Spring Boot的WebFlux依賴,在pom.xml
中應(yīng)該包含如下依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>
示例代碼
創(chuàng)建一個用戶模型(User.java)
public class User { private String id; private String name; private String email; // 構(gòu)造函數(shù)、getter和setter }
創(chuàng)建一個服務(wù)層接口(UserService.java)
這個接口定義了一個獲取用戶的方法,返回一個Mono<User>
,表示這是一個可能返回單個用戶對象的異步操作。
import reactor.core.publisher.Mono; public interface UserService { Mono<User> findUserById(String id); }
實現(xiàn)服務(wù)層(UserServiceImpl.java)
這個實現(xiàn)模擬從數(shù)據(jù)庫異步獲取用戶信息的操作。
import reactor.core.publisher.Mono; public class UserServiceImpl implements UserService { @Override public Mono<User> findUserById(String id) { // 模擬數(shù)據(jù)庫查詢操作 return Mono.just(new User(id, "John Doe", "johndoe@example.com")); } }
創(chuàng)建一個控制器(UserController.java)
這個控制器使用UserService
來獲取用戶信息,并通過HTTP提供這一服務(wù)。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user/{id}") public Mono<User> getUserById(@PathVariable String id) { // 使用UserService的方法獲取用戶信息,并返回 return userService.findUserById(id); } }
運行示例
在Spring Boot應(yīng)用中運行上述代碼,你可以使用如下HTTP GET請求來測試這個API:
GET http://localhost:8080/user/123
這個請求應(yīng)該返回類似于以下的JSON響應(yīng):
{ "id": "123", "name": "John Doe", "email": "johndoe@example.com" }
說明
在這個例子中,當HTTP請求/user/{id}
被接收時,UserController
會調(diào)用UserService
的findUserById
方法。該方法異步地返回一個包含用戶信息的Mono<User>
。由于整個數(shù)據(jù)處理流程是非阻塞的,Spring WebFlux框架能夠高效地處理來自客戶端的請求,即使在高并發(fā)場景下也能保持良好的性能。
總結(jié)
Reactor的這些特性使其成為構(gòu)建現(xiàn)代、高性能反應(yīng)式應(yīng)用的一個強大工具,特別是在需要處理高并發(fā)數(shù)據(jù)流的微服務(wù)和云應(yīng)用中。通過這種模式,Reactor模型能夠提供一種高效且強大的方式來構(gòu)建能夠處理高并發(fā)、高負載且需要低延遲響應(yīng)的現(xiàn)代應(yīng)用程序。這使得Spring Boot非常適合用來開發(fā)大規(guī)模的互聯(lián)網(wǎng)應(yīng)用,特別是在微服務(wù)架構(gòu)的環(huán)境中。
到此這篇關(guān)于詳解SpringBoot中如何使用Reactor模型的文章就介紹到這了,更多相關(guān)SpringBoot使用Reactor模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IntelliJ IDEA 2023.2正式發(fā)布新UI和Profiler轉(zhuǎn)正(最新推薦)
北京時間2023年7月26日,IntelliJ IDEA 2023.2正式發(fā)布,IntelliJ IDEA 2023.2 引入 AI Assistant(AI助手),通過一組由 AI 提供支持的功能助力開發(fā),今天給大家分享IntelliJ IDEA 2023.2正式發(fā)布新UI和Profiler轉(zhuǎn)正,感興趣的朋友一起看看吧2023-10-10利用Java簡單實現(xiàn)一個代碼行數(shù)統(tǒng)計器方法實例
這篇文章主要給大家介紹了關(guān)于如何利用Java簡單實現(xiàn)一個代碼行數(shù)統(tǒng)計器的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11mybatis框架order by作為參數(shù)傳入時失效的解決
這篇文章主要介紹了mybatis框架order by作為參數(shù)傳入時失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06基于jdk動態(tài)代理和cglib動態(tài)代理實現(xiàn)及區(qū)別說明
這篇文章主要介紹了基于jdk動態(tài)代理和cglib動態(tài)代理實現(xiàn)及區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Spring中基于Java的配置@Configuration和@Bean用法詳解
這篇文章主要介紹了Spring中基于Java的配置@Configuration和@Bean用法詳解,Spring中為了減少xml中配置,可以聲明一個配置類(例如SpringConfig)來對bean進行配置。,需要的朋友可以參考下2019-06-06Can''t use Subversion command line client:svn 報錯處理
這篇文章主要介紹了Can't use Subversion command line client:svn 報錯處理的相關(guān)資料,需要的朋友可以參考下2016-09-09Scala可變參數(shù)列表,命名參數(shù)和參數(shù)缺省詳解
這篇文章主要介紹了Scala可變參數(shù)列表,命名參數(shù)和參數(shù)缺省詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-06-06