Spring5 框架新功能
整個 Spring5 框架的代碼基於 Java8,運行時兼容 JDK9,許多不建議使用的類和方法在代碼庫中刪除了。
1、Spring 5.0 框架自帶了通用的日誌封裝
(1)Spring5 已經移除 Log4jConfigListener,官方建議使用 Log4j2
(2)Spring5 框架整合 Log4j2
第一步 引入 jar 包
第二步 創建 log4j2.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--日誌級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration 後面的 status 用於設置 log4j2 自身內部的信息輸出,可以不設置,當設置成 trace 時,可以看到 log4j2 內部各種詳細輸出-->
<configuration status="INFO">
<!--先定義所有的 appender-->
<appenders>
<!--輸出日誌信息到控制檯-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日誌輸出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然後定義 logger,只有定義 logger 並引入的 appender,appender 纔會生效!-->
<!--root:用於指定項目的根日誌,如果沒有單獨指定 Logger,則會使用 root 作爲默認的日誌輸出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
2、Spring5 框架核心容器支持@Nullable 註解
@Nullable 註解可以使用在方法、屬性、參數上面,表示方法返回可以爲空,屬性值可以爲空,參數值可以爲空
3、Spring5 核心容器支持函數式風格 GenericApplicationContext
//函數式風格創建對象,交給 spring 進行管理
@Test
public void testGenericApplicationContext() {
//1 創建 GenericApplicationContext 對象
GenericApplicationContext context = new GenericApplicationContext();
//2 調用 context 的方法對象註冊
context.refresh();
context.registerBean("user1",User.class,() -> new User());
//3 獲取在 spring 註冊的對象
// User user = (User)context.getBean("com.atguigu.spring5.test.User");
User user = (User)context.getBean("user1");
System.out.println(user);
}
4、Spring5 框架新功能——Webflux(類似於springMVC)
a)Webflux是 Spring5 添加新的模塊,用於 web 開發的,功能和 SpringMVC 類似的,Webflux 使用 當前一種比較流行的響應式編程出現的框架。
b)使用傳統 web 框架,比如 SpringMVC,這些基於 Servlet 容器,Webflux 是一種異步非阻塞的框架,異步非阻塞的框架在 Servlet3.1 以後才支持,核心是基於 Reactor 的相關 API 實現的。
解釋什麼是異步非阻塞 :
(1)異步和同步 (針對調用者)
同步: 調用者發送請求,如果等着對方迴應之後纔去做其他事情就是同步
異步: 調用者發送請求,如果發送請求之後不等着對方迴應就去做其他事情就是異步 √
(2)非阻塞和阻塞 (針對被調用者)
阻塞: 被調用者受到請求之後,做完請求任務之後纔給出反饋就是阻塞
非阻塞: 被調用者受到請求之後,受到請求之後馬上給出反饋然後再去做事情就是非阻塞 √
c)Webflux 特點
(1)第一 非阻塞式:在有限資源下,提高系統吞吐量和伸縮性,以 Reactor 爲基礎實現響應式編程
(2)第二 函數式編程:Spring5 框架基於 java8,Webflux 使用 Java8 函數式編程方式實現路由請求
d)SpringMVC和Webflux之間區別
相同:兩個框架都可以使用註解方式,都運行在 Tomcat 等容器中
不同:SpringMVC 採用命令式編程,Webflux 採用異步響應式編程
5、響應式編程(Java 實現)
概念:什麼是響應式編程 響應式編程是一種面向數據流和變化傳播的編程範式。這意味着可以在編程語言中很方便 地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播。
例子:電子表格程序就是響應式編程的一個例子。單元格可以包含字面值或類似"=B1+C1"的公 式,而包含公式的單元格的值會依據其他單元格的值的變化而變化。
a)Java8 及其之前版本是提供的觀察者模式兩個類 Observer 和 Observable實現
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
//添加觀察者
observer.addObserver((o,arg)->{
System.out.println("發生變化");
});
observer.addObserver((o,arg)->{
System.out.println("手動被觀察者通知,準備改變");
});
//這裏必須進行兩個方法調用纔可以響應!!
observer.setChanged(); //數據變化
observer.notifyObservers(); //通知
}
}
6、響應式編程(Reactor 實現)
(1)響應式編程操作中,Reactor 是滿足 Reactive 規範框架
(2)Reactor 有兩個核心類,Mono 和 Flux,這兩個類實現接口 Publisher,提供豐富操作符。Flux 對象實現發佈者,返回 N 個元素;Mono 實現發佈者,返回 0 或者 1 個元素
(3)Flux 和 Mono 都是數據流的發佈者,使用 Flux 和 Mono 都可以發出三種數據信號: 元素值,錯誤信號,完成信號,錯誤信號和完成信號都代表終止信號,終止信號用於告訴 訂閱者數據流結束了,錯誤信號終止數據流同時把錯誤信息傳遞給訂閱者
(4)代碼演示 Flux (返回多個元素)和 Mono(返回0或1個元素)
<!--第一步 引入依賴-->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.5.RELEASE</version>
</dependency>
//第二步 編程代碼
public static void main(String[] args) {
//just 方法直接聲明
Flux.just(1,2,3,4);
Mono.just(1);
//其他的方法
Integer[] array = {1,2,3,4};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
}
(5)三種信號特點
① 錯誤信號和完成信號都是終止信號,不能共存的
② 如果沒有發送任何元素值,而是直接發送錯誤或者完成信號,表示是空數據流
③ 如果沒有錯誤信號,沒有完成信號,表示是無限數據流
(6)調用 just 或者其他方法只是聲明數據流,數據流並沒有發出,只有進行訂閱之後纔會觸發數據流,不訂閱什麼都不會發生的!!
//just方法聲明後,進行訂閱
Flux.just(1,2,3,4).subscribe(System.out::print);
Mono.just(1).subscribe(System.out::print);
(7)操作符
概念: 對數據流進行一道道操作,成爲操作符,比如工廠流水線
a) map 元素映射爲新元素
b) flatMap 元素映射爲流;把每個元素轉換流,把轉換之後多個流合併大的流
7、SpringWebflux 執行流程和核心 API
a)SpringWebflux 基於 Reactor,默認使用容器是 Netty,Netty 是高性能的 NIO 框架,異步非阻塞的框架
b)SpringWebflux 核心控制器 DispatchHandler,實現接口 WebHandler 接口
c)SpringWebflux 裏面 DispatcherHandler負責請求的處理 ; HandlerMapping請求查詢到處理的方法 ;HandlerAdapter真正負責請求處理 ; HandlerResultHandler響應結果處理
d)SpringWebflux 實現函數式編程,兩個接口:RouterFunction(路由處理) 和 HandlerFunction(處理函數)
8、SpringWebflux(基於註解編程模型)
a)SpringWebflux 實現方式有兩種:註解編程模型和函數式編程模型
b)使用註解編程模型方式,和之前 SpringMVC 使用相似的,只需要把相關依賴配置到項目中, SpringBoot 自動配置相關運行容器,默認情況下使用 Netty 服務器
第一步 創建 SpringBoot 工程,引入 Webflux 依賴
第二步 配置啓動端口號
第三步 創建包和相關類
實體類
//實體類 public class User { private String name; private String gender; private Integer age; public User(String name, String gender, Integer age) { this.name = name; this.gender = gender; this.age = age; } public void setName(String name) { this.name = name; } //其他set和get略去... }
創建接口定義操作的方法
//用戶操作接口 public interface UserService { //根據id查詢用戶 Mono<User> getUserById(int id);//Mono返回單個或零個元素 //查詢所有用戶 Flux<User> getAllUser();//Flux返回多個元素 //添加用戶 Mono<Void> saveUserInfo(Mono<User> user); }
接口實現類
@Repository public class UserServiceImpl implements UserService { //創建map集合存儲數據 private final Map<Integer,User> users = new HashMap<>(); public UserServiceImpl() { this.users.put(1,new User("lucy","nan",20)); this.users.put(2,new User("mary","nv",30)); this.users.put(3,new User("jack","nv",50)); } //根據id查詢 @Override public Mono<User> getUserById(int id) { return Mono.justOrEmpty(this.users.get(id)); } //查詢多個用戶 @Override public Flux<User> getAllUser() { return Flux.fromIterable(this.users.values()); } //添加用戶 @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext(person -> { //向map集合裏面放值 int id = users.size()+1; users.put(id,person); }).thenEmpty(Mono.empty()); } }
創建 controller
@RestController public class UserController { //注入 service @Autowired private UserService userService; //id 查詢 @GetMapping("/user/{id}") public Mono<User> geetUserId(@PathVariable int id) { return userService.getUserById(id); } //查詢所有 @GetMapping("/user") public Flux<User> getUsers() { return userService.getAllUser(); } //添加 @PostMapping("/saveuser") public Mono<Void> saveUser(@RequestBody User user) { Mono<User> userMono = Mono.just(user); return userService.saveUserInfo(userMono); } }
c)SpringMVC 方式實現,同步阻塞的方式,基於 SpringMVC+Servlet+Tomcat
SpringWebflux 方式實現,異步非阻塞 方式,基於 SpringWebflux+Reactor+Netty
9、SpringWebflux(基於函數式編程模型)
(1)在使用函數式編程模型操作時候,需要自己初始化服務器
(2)基於函數式編程模型時候,有兩個核心接口:RouterFunction(實現路由功能,請求轉發 給對應的 handler)和 HandlerFunction(處理請求生成響應的函數)。核心任務定義兩個函數 式接口的實現並且啓動需要的服務器。
( 3 ) SpringWebflux 請 求 和 響 應 不 再 是 ServletRequest 和 ServletResponse ,而是 ServerRequest 和 ServerResponse
步驟:
第一步 把註解編程模型工程複製一份 ,保留 entity 和 service 內容
第二步 創建 Handler(具體實現方法)
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
//根據 id 查詢
public Mono<ServerResponse> getUserById(ServerRequest request) {
//獲取 id 值
int userId = Integer.valueOf(request.pathVariable("id"));
//空值處理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
//調用 service 方法得到數據
Mono<User> userMono = this.userService.getUserById(userId);
//把 userMono 進行轉換返回
//使用 Reactor 操作符 flatMap
return userMono.flatMap(person ->
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person)))
.switchIfEmpty(notFound);
}
//查詢所有
public Mono<ServerResponse> getAllUsers() {
//調用 service 得到結果
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(users,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request) {
//得到 user 對象
Mono<User> userMono = request.bodyToMono(User.class);
return
ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
第三步 初始化服務器,編寫 Router
//1 創建 Router 路由
public RouterFunction<ServerResponse> routingFunction() {
//創建 hanler 對象
UserService userService = new UserServiceImpl();
UserHandler handler = new UserHandler(userService);
//設置路由
return RouterFunctions.route(
GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
.andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
}
//2 創建服務器完成適配
public void createReactorServer() {
//路由和 handler 適配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//創建服務器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();//立即生效
}
//最終調用
public static void main(String[] args) throws Exception{
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//使用 WebClient 調用
public class Client {
public static void main(String[] args) {
//調用服務器地址
WebClient webClient = WebClient.create("http://127.0.0.1:5794");
//根據 id 查詢
String id = "1";
User userresult = webClient.get().uri("/users/{id}", id)
.accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
System.out.println(userresult.getName());
//查詢所有
Flux<User> results = webClient.get().uri("/users")
.accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
results.map(stu -> stu.getName())
.buffer().doOnNext(System.out::println).blockFirst();
}
}