學透Spring Boot — 009. Spring Boot的四種 Http 客戶端

目錄

常見的HttpClient

Spring 提供的HttpClient

RestTemplate

Spring 提供的模板類 XXXTemplate

RestTemplate的使用

RestTemplate的使用技巧

RestTemplate的問題

RestClient

?RestClinet的基本使用

RestClient的自動配置

RestClient 序列化對象

異常處理 onStatus

更精細的控制 exchange

HTTP消息轉換 - Jackson Json 視圖

RestClient真正使用的HTTP庫

非異步調用

WebClient

簡單使用

請求攔截器

深度配置WebClient

HTTP接口

配置和使用


常見的HttpClient

我們項目中通常要調用內部或者外部的HTTP接口,這時候就需要用到Http Client, 以前我們經常用第三方的組件比如:

  1. Apache HttpClient
  2. OkHttp
  3. Spring Cloud提供的Feign Http客戶端

但是對于一般的項目,我們直接使用Spring 提供的HttpClient客戶端就可以滿足大部分的場景了。

所以嚴格上說,本文介紹的不是Spring Boot的特性,而是Spring提供的特性。

Spring 提供的HttpClient

Spring框架提供了幾種調用REST API的方式:

  • RestTemplate:調用API的同步客戶端
  • RestClient:異步客戶端
  • WebClient:異步并且是響應式的客戶端
  • Http Interface:基于動態代理的聲明式接口

說明:

本文我們會用一個開源的HTTP REST API用來測試:

https://jsonplaceholder.typicode.com/users

為此,我們定義了Java類接收json響應

@Builder
@Data
public class TypiUser {private Integer id;private String name;private String username;private String email;private String phone;private String website;private TypiAddress address;
}@Builder
@Data
public class TypiAddress {private String street;private String suite;private String city;private String zipcode;
}

Tips:

使用builder構造對象時,報錯:

java: cannot find symbolsymbol:   method builder()location: class lab.rest.TypiUser

后來打開target/classes 看編譯后的類才發現TypiUser沒有編譯正確,所以執行 `mvn clean build` 后可以解決報錯問題。

發現另一個地方設置也問題,應該不要勾選Processor Path

RestTemplate

Spring 提供的模板類 XXXTemplate

熟悉Spring框架的開發都知道,Spring提供了各種XXXTemplate,使用模板方法模式封裝了復雜的底層操作,簡化了我們對外部組件的操作和使用。

常見的有:

  • JdbcTemplate:封裝了JDBC的底層操作,簡化了數據庫操作
  • JmsTemplate:封裝了對JMS API的操作,簡化了收發消息的操作
  • ElasticsearchRestTemplate:是Spring Data ES模塊的一部分,簡化了對ES的操作
  • RedisTemplate:是Spring Data Redis模塊的一部分,簡化了對Redis的操作
  • HibernateTemplate:簡化了對Hibernate的CRUD操作
  • ……

擴展:模板方法模式

這些XXXTemplate用到的是模版方法模式

  • 在在抽象父類中定義了算法的骨架
  • 然后子類中實現某些步驟(抽象方法)

RestTemplate的使用

而本章節,我們要介紹的是RestTemplate,它是Spring用于進行HTTP 發送請求和接收響應的客戶端工具。使得我們調用REST API變得非常的簡單。

使用RestTemplate發送post請求

@Test
public void basicTest(){RestTemplate restTemplate = new RestTemplate();String url = "https://jsonplaceholder.typicode.com/users";TypiUser user = TypiUser.builder().name("Joe").username("joe").email("joe@gmail.com").phone("123456789").website("joe.com").address(TypiAddress.builder().street("street").suite("suite").city("city").zipcode("zipcode").build()).build();ResponseEntity<TypiUser> response = restTemplate.postForEntity(url, user, TypiUser.class);TypiUser body = response.getBody();System.out.printf("用戶創建成功,id:%s, name:%s%n", body.getId(), body.getName());
}

可以看到使用RestTemplate 發送RestTemplate非常簡單。

我們在Web開發中通常會通過配置類來配置RestTemplate實例

@Configuration
public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate(RestTemplateBuilder builder) {return builder.setConnectTimeout(Duration.ofSeconds(10)) //設置連接超時時間.setReadTimeout(Duration.ofSeconds(10)) // 設置讀取超時時間.build();}
}

然后注入RestTemplate直接使用

@Service
public class TypiUserService {private final RestTemplate restTemplate;public TypiUserService(RestTemplate restTemplate) {this.restTemplate = restTemplate;}public TypiUser getUser(Integer id) {return restTemplate.getForObject("https://jsonplaceholder.typicode.com/users/" + id,TypiUser.class);}
}

RestTemplate的使用技巧

一般場景,我們可以直接用getXXX方法發起get請求,通過postForEntity發起post請求,通過put()方法發起put請求,通過delete發起delete請求。

但某些時候我們想更精準的控制client的時候,就需要用到更原始的方法exchange(),它允許我們指定HTTP方法,處理請求頭和請求體。

public TypiUser getUserByExchange(String id) {String url = "https://jsonplaceholder.typicode.com/users/" + id;ResponseEntity<TypiUser> response = restTemplate.exchange(url, HttpMethod.GET, null, TypiUser.class);TypiUser user = response.getBody();System.out.println(user);System.out.println(response.getStatusCode());System.out.println(response.getHeaders());return user;
}

異常處理

接口如果處理有問題,RestTemplate 也會拋出一些異常,尤其是在遇到錯誤響應時。常見的異常包括:

  1. HttpClientErrorException:用于 4xx 錯誤。
  2. HttpServerErrorException:用于 5xx 錯誤。
  3. ResourceAccessException:網絡或連接問題時拋出。

我們需要捕獲并處理這些異常。

一種方式是我們直接在service中捕獲

public TypiUser getUser(Integer id) {try{return restTemplate.getForObject("https://jsonplaceholder.typicode.com/users/" + id,TypiUser.class);}catch (HttpClientErrorException e){throw new RuntimeException("客戶端異常", e);}catch (HttpServerErrorException e){throw new RuntimeException("服務器端異常", e);}catch (RestClientException e) {throw new RuntimeException("Rest Client異常", e);}
}

但是一般沒人這么干,因為如果我們有100個方法調用rest client,那不是得寫100遍。所以我們一般會統一的處理異常,使用Spring提供的@ControllerAdvice 和 @ExceptionHandler全局捕獲并處理異常。

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(HttpClientErrorException.class)public ResponseEntity<ApiErrorResponse> handleHttpClientError(HttpClientErrorException e) {ApiErrorResponse errorResponse = new ApiErrorResponse(e.getStatusCode().value(),"Client Error",e.getResponseBodyAsString());return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(e.getStatusCode().value()));}// 略
}

不僅僅是RestTemplate,其它的Client也需要這種全局的異常處理。

RestTemplate的問題

RestTemplate簡單,但是也存在一些問題。

最重要的一點是它是同步的,它的每個請求都會阻塞直到收到響應。因為每個請求都占用一個線程,所以當大量請求同時發起時,導致系統的線程數會很快被耗盡,所以他的并發性能比較低。

RestClient

Spring引入了更現代化的HTTP客戶端 RestClient.

它本身也是同步的,不支持異步。但是它支持流式的調用方式,相比RestTemplate提供一個方法發起請求,流式調用更容易使用和控制。

?RestClinet的基本使用

@Service
public class TypiRestClientService {private final RestClient.Builder builder;private RestClient restClient;public TypiRestClientService(RestClient.Builder builder) {this.builder = builder;}// 使用 @PostConstruct 注解在 Spring 完成構造器注入后再進行初始化@PostConstructpublic void init() {// 使用 builder 創建 RestClient 實例,進行初始化this.restClient = this.builder.baseUrl("https://jsonplaceholder.typicode.com").build();}public TypiUser getUser(Integer id) {return restClient.get().uri("/users/" + id).retrieve().body(TypiUser.class);}
}

RestClient的自動配置

我們都沒有顯示的配置RestClient.Builder,它是怎么自動注入的呢?

還是那一套,SpringBoot的自動配置。

在autoconfiguration包下面

可以看到,自動配置了RestClient和RestTemplate

這里自動配置了Builder Bean

@ConditionalOnClass(RestClient.class) 這個注解表示,RestClientAutoConfiguration 只會在類路徑中存在 RestClient 類時才會被加載

@Conditional(NotReactiveWebApplicationCondition.class)如果是反應式應用,RestClientAutoConfiguration 就不會被加載。

@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,SslAutoConfiguration.class })
@ConditionalOnClass(RestClient.class)
@Conditional(NotReactiveWebApplicationCondition.class)
public class RestClientAutoConfiguration {@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@ConditionalOnMissingBeanRestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) {return restClientBuilderConfigurer.configure(RestClient.builder());}
}

直接注入RestClient行不行呢?

@Service
public class TypiRestClientService {private final RestClient restClient;public TypiRestClientService(RestClient restClient) {this.restClient = restClient;}

當然不行,因為自動配置類中并沒有配置RestClient的Bean.

啟動報錯如下:

Action:

Consider defining a bean of type 'org.springframework.web.client.RestClient' in your configuration.

RestClient 序列化對象

requestbody和responseBody會自動轉對象

    public TypiUser saveUser() {TypiUser user = TypiUser.builder().name("Joe").username("joe").email("joe@gmail.com").address(TypiAddress.builder().city("Beijing").build()).build();TypiUser result = restClient.post().uri("/users").body(user).retrieve().body(TypiUser.class);return result;}

retrieve方法之前的body()是request body, 后面的body()是 response body.

異常處理 onStatus

它提供了很優雅的異常處理

TypiUser result = restClient.post().uri("/users").body(user).retrieve().onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {throw new RuntimeException("status code: " + response.getStatusCode());})).body(TypiUser.class);

也是拋出異常,在全局類里面捕獲并處理異常。

更精細的控制 exchange

如果想要更精準的控制請求和響應,我們可以利用更底層的方法來發起請求。

    TypiUser result = restClient.post().uri("/users").body(user).exchange((request, response) -> {if(response.getStatusCode().is2xxSuccessful()){return response.bodyTo(TypiUser.class);} else if(response.getStatusCode().is4xxClientError()){throw new RuntimeException("客戶端錯誤,status code: " + response.getStatusCode());} else {throw new RuntimeException("其它錯誤,status code: " + response.getStatusCode());}});

雖然可以更精細操作,但是操作得都是底層的request和response對象,也變得更麻煩了。世界上沒有絕對完美的事情。

HTTP消息轉換 - Jackson Json 視圖

發送請求時,我們不想把整個對象的所有字段都序列化成json后發送,而是序列化部分字段,可以做到的嗎?

當然可以,最笨的方法是直接新建一個新的對象,只包含部分屬性,然后用BeanUtils.copy復制屬性。

因為我們用的是Jackson,所以可以更優雅

使用Jackson的json視圖

MappingJacksonValue value = new MappingJacksonValue(user);
value.setSerializationView(TypiUser.TypiUserView.class);
TypiUser result = restClient.post().uri("/users").body(value).retrieve().onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {throw new RuntimeException("status code: " + response.getStatusCode());})).body(TypiUser.class);return result;

第一次聽到這個技術,挺有意思。

RestClient真正使用的HTTP庫

RestClient是對外提供的客戶端,要執行HTTP請求,它底層還是得依賴其它HTTP庫。

很像Slf4j, 它是個門面,底層是需要用log4j等日志框架的。

在 Spring 中,RestClient 通過適配不同的 HTTP 庫來執行 HTTP 請求。這些庫的適配是通過實現 ClientRequestFactory 接口來實現的。

具體實現有:

  • JdkClientHttpRequestFactory:使用的是?Java的?HttpClient
  • HttpComponentsClientHttpRequestFactory 使用的是Apache HTTP Components HttpClient
  • JettyClientHttpRequestFactory:使用Jetty的?HttpClient
  • ReactorNettyClientRequestFactory:使用Reactor Netty的?HttpClient
  • SimpleClientHttpRequestFactory:簡單的實現

我們什么也沒有配置,使用默認配置,看看是什么工廠類:

可以看到,使用的是JDK的HTTP Client。

非異步調用

RestClient也是同步HTTP客戶端。但是對比RestTemplate,API是流式的更容易使用。如果沒有異步的場景需求,我們可以使用RestClient。

WebClient

RestTemplate和RestClient都是同步的,WebClient是非阻塞 響應式的HTTP 客戶端。

它是Spring5引入的,用來替代RestTemplate。

簡單使用

WebClient是Spring WebFlux模塊的功能,所以我們需要先引入依賴

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>

WebClient也是SpringBoot自動配置的,我們直接注入使用即可

@Service
public class TypiWebClientService {private final WebClient webClient;public TypiWebClientService(WebClient.Builder builder) {this.webClient = builder.baseUrl("https://jsonplaceholder.typicode.com").build();}
}

自動配置類

配置條件是classpath下找到了WebClient就自動配置WebClient.Builder這個Bean。所以我們注入的是WebClient.Builder這個Bean 而不是WebClient。

這塊配置配置和RestClient一樣!

@AutoConfiguration(after = { CodecsAutoConfiguration.class, ClientHttpConnectorAutoConfiguration.class })
@ConditionalOnClass(WebClient.class)
public class WebClientAutoConfiguration {@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@ConditionalOnMissingBeanpublic WebClient.Builder webClientBuilder(ObjectProvider<WebClientCustomizer> customizerProvider) {WebClient.Builder builder = WebClient.builder();customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));return builder;}

發起異步調用

    public TypiUser getUser(Integer id) {Mono<TypiUser> mono = webClient.get().uri("/users/{id}", id).retrieve().bodyToMono(TypiUser.class).doOnTerminate(() -> System.out.println("調用結束"));System.out.println("繼續調用");TypiUser user = mono.block(); // 阻塞等待System.out.println("獲取到用戶:" + user);return user;}

繼續調用
調用結束
獲取到用戶:TypiUser(id=1, name=Leanne Graham, username=Bret, email=Sincere@april.biz, phone=1-770-736-8031 x56442, website=hildegard.org, address=TypiAddress(street=Kulas Light, suite=Apt. 556, city=Gwenborough, zipcode=92998-3874))

可以看到,發起請求后,線程并沒有blocked住,而是繼續往下走,直到執行到block()方法時,線程才會阻塞。

以前看了這么多八股文,很難理解異步WebClient。其實只要寫個最簡單的例子,就非常容易理解了。Java八股害死人。

請求攔截器

我們可以攔截請求,做一些特殊的處理。

    public TypiWebClientService(WebClient.Builder builder) {this.webClient = builder.baseUrl("https://jsonplaceholder.typicode.com").filter((request, next) -> {System.out.println("開始調用,路徑 " + request.url().toString());return next.exchange(request);}).build();}

這樣所有的請求,都會執行這個操作

繼續調用
開始調用,路徑 https://jsonplaceholder.typicode.com/users/1
調用結束

深度配置WebClient

在配置類中配置WebClient Bean

配置連接池和選擇底層使用的HTTP客戶端。

@Configuration
public class WebClientConfig {@Beanpublic WebClient webClient() {ConnectionProvider provider = ConnectionProvider.builder("custom").maxConnections(50)  // 設置最大連接數.build();HttpClient httpClient = HttpClient.create(provider).responseTimeout(Duration.ofSeconds(5))  // 設置響應超時.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).baseUrl("https://jsonplaceholder.typicode.com").build();}
}

這里我們使用的HTTP Client是Netty的客戶端。

并設置了連接池的大小還有HTTP的響應時間。

這樣我們可以直接注入使用WebClient了

@Service
public class TypiWebClientService {private final WebClient webClient;public TypiWebClientService(WebClient webClient) {this.webClient = webClient;}

HTTP接口

Spring允許我們通過Java接口的方式調用HTTP服務。我們不需要寫代碼顯示調用HTTP,而是通過注解聲明即可。

底層是利用動態代理技術,簡化了遠程 HTTP 調用。

配置和使用

首先,我們定義一個接口,并用注解聲明

public interface TypiUserRestService {@GetExchange("/users/{id}")TypiUser getUser(@PathVariable Integer id);
}

然后,我們需要創建一個代理,底層還是需要其它HTTP庫的

當然我們也可以選用其它的庫比如WebClient等

@Configuration
public class TypiUserRestServiceConfig {@Beanpublic TypiUserRestService config(){RestClient restClient = RestClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build();RestClientAdapter adapter = RestClientAdapter.create(restClient);HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();return factory.createClient(TypiUserRestService.class);}
}

接下來,我們就可以在我們的Controller中注入直接使用了。

@RestController
@RequestMapping("/typi")
public class TypiUserController {private final TypiUserRestService typiUserRestService;@GetMapping("/v4/user/{id}")public TypiUser getUser4(@PathVariable Integer id){return typiUserRestService.getUser(id);}

這種聲明式的調用,讓我們的代碼顯得非常簡潔!

很類似Spring Cloud Feign的調用方式。

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

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

相關文章

leetcode117 填充每個節點的下一個右側節點指針2

LeetCode 116 和 117 都是關于填充二叉樹節點的 next 指針的問題&#xff0c;但它們的區別在于 樹的類型 不同&#xff0c;117與 116 題類似&#xff0c;但給定的樹是 普通二叉樹&#xff08;不一定完全填充&#xff09;&#xff0c;即某些節點可能缺少左或右子節點。 樹的結構…

軟考系統架構師 — 4 嵌入式軟件

目錄 4.1 考點分析 4.2 嵌入式微處理器 4.2.1嵌入式微處理器體系結構 5.2.2 嵌入式微處理器分類 4.2.3 多核處理器 4.3 嵌入式軟件 4.4 嵌入式系統 4.4.1 嵌入式系統的組成 4.4.2 嵌入式系統分類 4.4.3 嵌入式數據庫系統DBMS 4.4.4 嵌入式操作系統OS 4.4.5 嵌入式實…

RocketMQ 中的 ProducerManager 組件剖析

一、引言 在分布式系統的消息傳遞領域&#xff0c;RocketMQ 以其高性能、高可用性和強大的擴展性脫穎而出。ProducerManager 作為 RocketMQ 中的一個關鍵組件&#xff0c;在消息生產環節發揮著至關重要的作用。它負責管理消息生產者&#xff08;Producer&#xff09;的生命周期…

k8s進階之路:本地集群環境搭建

概述 文章將帶領大家搭建一個 master 節點&#xff0c;兩個 node 節點的 k8s 集群&#xff0c;容器基于 docker&#xff0c;k8s 版本 v1.32。 一、系統安裝 安裝之前請大家使用虛擬機將 ubuntu24.04 系統安裝完畢&#xff0c;我是基于 mac m1 的系統進行安裝的&#xff0c;所…

深度學習數據集劃分比例多少合適

在機器學習和深度學習中&#xff0c;測試集的劃分比例需要根據數據量、任務類型和領域需求靈活調整。 1. 常規劃分比例 通用場景 訓練集 : 驗證集 : 測試集 60% : 20% : 20% 適用于大多數中等規模數據集&#xff08;如數萬到數十萬樣本&#xff09;&#xff0c;平衡了訓練數…

【TS學習】(15)分布式條件特性

在 TypeScript 中&#xff0c;分布式條件類型&#xff08;Distributive Conditional Types&#xff09; 是一種特殊的行為&#xff0c;發生在條件類型作用于裸類型參數&#xff08;Naked Type Parameter&#xff09; 時。這種特性使得條件類型可以“分布”到聯合類型的每個成員…

NSSCTF [HGAME 2023 week1]simple_shellcode

3488.[HGAME 2023 week1]simple_shellcode 手寫read函數shellcode和orw [HGAME 2023 week1]simple_shellcode (1) motalymotaly-VMware-Virtual-Platform:~/桌面$ file vuln vuln: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpret…

PostgreSQL的擴展(extensions)-常用的擴展-pg_dirtyread

PostgreSQL的擴展&#xff08;extensions&#xff09;-常用的擴展-pg_dirtyread pg_dirtyread 是 PostgreSQL 的一個特殊擴展&#xff0c;它允許讀取已被刪除但尚未被 VACUUM 清理的數據行&#xff0c;是數據恢復的重要工具。 原理&#xff1a; pg_dirtyread 通過直接訪問表的…

linux3 mkdir rmdir rm cp touch ls -d /*/

Linux 系統的初始目錄結構遵循 FHS&#xff08;Filesystem Hierarchy Standard&#xff0c;文件系統層次標準&#xff09;&#xff0c;定義了每個目錄的核心功能和存儲內容。以下是 Linux 系統初始安裝后的主要目錄及其作用&#xff1a; 1. 核心系統目錄 目錄用途典型內容示例…

Bazel中的Symbol, Rule, Macro, Target, Provider, Aspect 等概念

學習Bazel &#xff0c;就要學習Bazel 的規則定義&#xff0c; 弄清各個概念是重要的一個步驟。 在 Bazel 規則定義中&#xff0c;Symbol、Rule 和 Macro 是常見的概念。除此之外&#xff0c;Bazel 還有 Target、Provider、Aspect Repository、Package、 Workspace、 Configura…

深入探究 Hive 中的 MAP 類型:特點、創建與應用

摘要 在大數據處理領域,Hive 作為一個基于 Hadoop 的數據倉庫基礎設施,提供了方便的數據存儲和分析功能。Hive 中的 MAP 類型是一種強大的數據類型,它允許用戶以鍵值對的形式存儲和操作數據。本文將深入探討 Hive 中 MAP 類型的特點,詳細介紹如何創建含有 MAP 類型字段的表…

基于Java的區域化智慧養老系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘 要 時代在飛速進步&#xff0c;每個行業都在努力發展現在先進技術&#xff0c;通過這些先進的技術來提高自己的水平和優勢&#xff0c;區域化智慧養老系統當然不能排除在外。區域化智慧養老系統是在實際應用和軟件工程的開發原理之上&#xff0c;運用Java語言、JSP技術以及…

關于JVM和OS中的指令重排以及JIT優化

關于JVM和OS中的指令重排以及JIT優化 前言&#xff1a; 這東西應該很重要才對&#xff0c;可是大多數博客都是以訛傳訛&#xff0c;全是錯誤&#xff0c;尤其是JVM會對字節碼進行重排都出來了&#xff0c;明明自己測一測就出來的東西&#xff0c;寫出來誤人子弟… 研究了兩天&…

VS2022遠程調試Linux程序

一、 1、VS2022安裝參考 VS Studio2022安裝教程&#xff08;保姆級教程&#xff09;_visual studio 2022-CSDN博客 注意&#xff1a;勾選的時候&#xff0c;要勾選下方的選項&#xff0c;才能調試Linux環境下運行的程序&#xff01; 2、VS2022遠程調試Linux程序測試 原文參…

WPF設計學習記錄滴滴滴4

<Button x:Name"btn"Content"退出"Width" 100"Height"25"Click"btn_Click" IsDefault"True"/> <Button x:Name"btn" <!-- 控件標識&#xff1a;定義按鈕的實例名稱為"btn&…

JVM 有哪些垃圾回收器

垃圾收集算法 標記-復制算法(Copying): 將可用內存按容量劃分為兩個區域,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面, 然后再把已使用過的內存空間一次清理掉。 標記-清除算法(Mark-Sweep): 算法分為“標記” 和“清除”兩個…

React DndKit 實現類似slack 類別、頻道拖動調整位置功能

一周調試終于實現了類 slack 類別、頻道拖動調整位置功能。 歷經四個版本迭代。 實現了類似slack 類別、頻道拖動調整功能 從vue->react &#xff1b;更喜歡React的生態及編程風格&#xff0c;新項目用React來重構了。 1.zustand全局狀態 2.DndKit 拖動 功能視頻&…

新浪財經股票每天10點自動爬取

老規矩還是先分好三步&#xff0c;獲取數據&#xff0c;解析數據&#xff0c;存儲數據 因為股票是實時的&#xff0c;所以要加個cookie值&#xff0c;最好分線程或者爬取數據時等待爬取&#xff0c;不然會封ip 廢話不多數&#xff0c;直接上代碼 import matplotlib import r…

使用Android 原生LocationManager獲取經緯度

一、常用方案 1、使用LocationManager GPS和網絡定位 缺點&#xff1a;個別設備,室內或者地下停車場獲取不到gps定位,故需要和網絡定位相結合使用 2、使用Google Play服務 這種方案需要Android手機中有安裝谷歌服務,然后導入谷歌的第三方庫&#xff1a; 例如&#xff1a;i…