当前位置: 首页 > news >正文

学透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的调用方式。

相关文章:

学透Spring Boot — 009. Spring Boot的四种 Http 客户端

目录 常见的HttpClient Spring 提供的HttpClient RestTemplate Spring 提供的模板类 XXXTemplate RestTemplate的使用 RestTemplate的使用技巧 RestTemplate的问题 RestClient RestClinet的基本使用 RestClient的自动配置 RestClient 序列化对象 异常处理 onStatus …...

STM32单片机入门学习——第14节: [6-2] 定时器定时中断定时器外部时钟

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.04 STM32开发板学习——第14节: [6-2] 定时器定时中断&定时器外部时钟 前言开发…...

Python 元组

Python 元组 元组的介绍 元组和列表很像&#xff0c;都是用来保存多个数据使用一对小括号()来表示一个元组元组和列表的区别在于&#xff0c;列表是可变数据类型&#xff0c;而元组是不可变数据类型 nums (9, 4, 3, 1, 9, 7, 6, 9, 3, 9) print(nums) # (9, 4, 3, 1, 9, 7…...

Linux系统编程:进程管理、内存对比与树莓派应用

一、认识进程和线程&#xff0c;在Linux系统下查看系统中各进程的编号pid并终止一个进程pid 1.进程和线程 ​​进程​​&#xff1a;操作系统分配资源&#xff08;如内存、CPU时间片&#xff09;的基本单位。每个进程有独立的内存空间&#xff0c;进程间通信需要较复杂的机制…...

JSON介绍

JSON 的核心特点​​ ​​易读易写​​&#xff1a;纯文本格式&#xff0c;结构清晰&#xff08;像“键值对”的集合&#xff09;。​​通用性强​​&#xff1a;任何语言都能解析或生成 JSON。​​用途广泛​​&#xff1a;常用于前后端数据传输、配置文件、API 接口等。 &am…...

【Cursor】切换主题

右键顶部&#xff0c;把菜单栏勾上 首选项-主题-颜色主题 选择和喜欢的颜色主题即可&#xff0c;一般是“现代深色”...

【C++11(上)】—— 我与C++的不解之缘(三十)

一、C11 这里简单了解一下C发展好吧&#xff1a; C11是C的第二个大版本&#xff0c;也是自C98以来最重要的一个版本。 它引入了大量的更改&#xff0c;它曾被人们称为C0x&#xff0c;因为它被期待在2010年之前发布&#xff1b;但在2011年8月12日才被采纳。 C03到C11花了8年时间…...

python如何把列表中所有字符变成小写

在Python中&#xff0c;你可以使用列表推导式&#xff08;list comprehension&#xff09;结合字符串的.lower()方法&#xff0c;将列表中的所有字符串元素转换为小写。以下是一个示例&#xff1a; # 定义一个包含字符串的列表 strings ["Hello", "WORLD"…...

DEAP数据集介绍

DEAP数据集介绍 0. 数据集摘要1. 文件列表2. 文件详细信息2.1 Online_ratings2.2 Video_list2.3 Participant_ratings2.4 Participant_questionnaire2.5 Face_video.zip2.6 Data_original.zip2.7 Data_preprocessed_matlab.zip 和 Data_preprocessed_python.zip 3. References欢…...

基于RDK X3的“校史通“机器人:SLAM导航+智能交互,让校史馆活起来!

视频标题&#xff1a; 【校史馆の新晋顶流】RDK X3机器人&#xff1a;导览员看了直呼内卷 视频文案&#xff1a; 跑得贼稳团队用RDK X3整了个大活——给校史馆造了个"社牛"机器人&#xff01; 基于RDK X3开发板实现智能导航与语音交互SLAM技术让机器人自主避障不…...

JavaScript基础-window.localStorage

在现代Web开发中&#xff0c;数据存储对于提升用户体验至关重要。window.localStorage 是一种简单而强大的客户端存储机制&#xff0c;允许网页以键值对的形式持久化保存数据。与 sessionStorage 不同&#xff0c;localStorage 中的数据不会因浏览器标签页关闭或刷新而丢失&…...

在航电系统中提高可靠性的嵌入式软件设计

1.总线余度设计 数据传输采用双余度总线设计&#xff0c;CANFD为主&#xff0c;RS485为备。发送方将相同的数据分别通过双总线来发送&#xff0c;接收方优先处理主线数据。由于总线上数据频率固定&#xff0c;可设置定时器监控主总线的数据&#xff0c;当定时器超时后&#xff…...

H.266/VVC SCC技术学习:块差分脉冲编码调整(block differential pulse coded modulation, BDPCM)

近年来&#xff0c;屏幕内容视频广泛用于多媒体应用&#xff0c;例如远程桌面,屏幕共享等。由于屏幕内容视频的特性与自然视频有较大区别&#xff0c;VVC中使用了帧内块复制&#xff08;intra block copy, 即IBC&#xff09;, 调色板&#xff08;Palette&#xff09;&#xff0…...

网络编程—Socket套接字(TCP)

上篇文章&#xff1a; 网络编程—Socket套接字&#xff08;UDP&#xff09;https://blog.csdn.net/sniper_fandc/article/details/146923670?fromshareblogdetail&sharetypeblogdetail&sharerId146923670&sharereferPC&sharesourcesniper_fandc&sharefro…...

数据结构:二叉树(三)·(重点)

二叉树的存储结构 ⼆叉树⼀般可以使⽤两种结构存储&#xff0c;⼀种顺序结构&#xff0c;⼀种链式结构。 顺序结构 顺序结构存储就是使⽤数组来存储&#xff0c;⼀般使⽤数组只适合表⽰完全⼆叉树&#xff0c;因为不是完全⼆叉树会有 空间的浪费&#xff0c;完全⼆叉树更适合…...

StyleTTS 2:文本到语音(Text-to-Speech, TTS)模型

StyleTTS 2 是一种先进的文本到语音&#xff08;Text-to-Speech, TTS&#xff09;模型&#xff0c;通过结合风格扩散&#xff08;style diffusion&#xff09;和对抗训练&#xff08;adversarial training&#xff09;&#xff0c;利用大规模语音语言模型&#xff08;Speech La…...

痉挛性斜颈康复路,饮食要点来相助

痉挛性斜颈患者除了接受正规治疗&#xff0c;合理饮食对缓解症状、促进康复也至关重要。 高蛋白质食物是饮食中的重点。像鸡蛋&#xff0c;富含人体必需的氨基酸&#xff0c;其组成与人体组成模式接近&#xff0c;易于吸收。每天吃 1 - 2 个鸡蛋&#xff0c;能为身体补充修复肌…...

谷歌 Gemini 2.5 Pro 免费开放

2025 年 3 月 30 日&#xff0c;谷歌宣布将最新的 Gemini AI 旗舰模型 Gemini 2.5 Pro 免费向所有 Gemini 应用用户开放。以下是关于此次免费开放的一些具体信息1&#xff1a; 背景&#xff1a;此前&#xff0c;Gemini 2.5 Pro 仅向支付 19.99 美元月费的 Gemini Advanced 用户…...

规则引擎Drools

1.规则引擎概述 1.1 什么是规则引擎 规则引擎 全称为业务规则管理系统&#xff0c;英文名为BRMS&#xff0c;规则引擎的主要思想是将应用程序中的业务决策部分分离出来&#xff0c;并使用预定义的语义模块编写业务规则&#xff0c;由用户或开发者在需要时进行配置和管理。 需…...

第三季:挪威

挪威 挪威是北欧的一个国家&#xff0c;位于斯堪的纳维亚半岛的西部。以下是关于挪威的详细介绍&#xff1a; 地理位置与自然环境 位置&#xff1a;挪威位于北欧&#xff0c;东邻瑞典&#xff0c;东北与芬兰和俄罗斯接壤&#xff0c;西濒挪威海&#xff0c;北临巴伦支海。地…...

搜索与图论 树的深度优先遍历 树的重心

树的一种特殊的图&#xff0c;无环连通图 图还分为有向图&#xff0c;无向图 但是无向图其实也是特殊的有向图 &#xff08;a指向b&#xff0c;b也指向a&#xff0c;每个连接节点都如此&#xff0c;则是无向图&#xff09; 那我们只需要讨论有向图 有向图的分类 邻接矩阵 …...

ORA-09925 No space left on device 问题处理全过程记录

本篇文章关键字&#xff1a;linux、oracle、审计、ORA-09925 一、故障现像 朋友找到我说是他们备份软件上报错。 问题比较明显&#xff0c;ORA-09925&#xff0c;看起来就是空间不足导致的 二、问题分析过程 这里说一下逐步的分析思路&#xff0c;有个意外提前说一下就是我…...

Java开发者の模型召唤术:LangChain4j咏唱指南(三)

Java开发者の模型召唤术&#xff1a;LangChain4j咏唱指南(三) 往期回顾&#xff1a; Java开发者の模型召唤术&#xff1a;LangChain4j咏唱指南(一)Java开发者の模型召唤术&#xff1a;LangChain4j咏唱指南(二) 上两期博客中简单的为大家介绍了 langchain4j是什么、java 集成…...

【leetcode100】动态规划Java版本

70. 爬楼梯 题目 思考的时候觉得情况很多&#xff0c;无从下手&#xff0c;卡在了找推导公式这一步。 看了随想录后知道以简单的三个阶梯来推导dp公式&#xff0c;为什么不是四个&#xff0c;五个为一组呢&#xff1f;因为题目要求的只能爬1个阶梯&#xff0c;或者2个阶梯&…...

RSA和ECC在密钥长度相同的情况下哪个更安全?

​现在常见的SSL证书&#xff0c;如&#xff1a;iTrustSSL都支持RSA和ECC的加密算法&#xff0c;正常情况下RAS和ECC算法该如何选择呢&#xff1f;实际上在密钥长度相同的情况下&#xff0c;ECC&#xff08;椭圆曲线密码学&#xff09;通常比RSA&#xff08;Rivest-Shamir-Adle…...

YOLO 获取 COCO 指标终极指南 | 从标签转换到 COCOAPI 评估 (训练/验证) 全覆盖【B 站教程详解】

✅ YOLO 轻松获取论文 COCO 指标&#xff1a;AP&#xff08;small&#xff0c;medium&#xff0c;large &#xff09;| 从标签转换到 COCOAPI 评估 (训练/验证) 全覆盖 文章目录 一、摘要二、为什么需要 COCO 指标评估 YOLO 模型&#xff1f;三、核心挑战与解决方案 (视频教程核…...

【算法竞赛】dfs+csp综合应用(蓝桥2023A9像素放置)

目录 一、 题目 二、思路 &#xff08;1&#xff09;算法框架选择 &#xff08;2&#xff09;剪枝策略 具体来说就是&#xff1a; 三、代码 (1) 数据读取与初始化 (2) 检查当前填充是否符合要求 (3) 递归 DFS 进行填充 (4) 读取输入 & 调用 DFS (5) 完整代码 一…...

3D点云配准RPM-Net模型解读(附论文+源码)

RPM-Net 总体流程代码数据预处理模型计算 α α α和 β β β特征提取变换矩阵计算损失 论文链接&#xff1a;RPM-Net: Robust Point Matching using Learned Features 官方链接&#xff1a;RPMNet 老规矩&#xff0c;先看看效果。 看看论文里给的对比图 总体流程 在学…...

23种设计模式-行为型模式-命令

文章目录 简介问题解决代码核心设计优势 总结 简介 命令是一种行为设计模式&#xff0c; 它能把请求转换为一个包含与请求相关的所有信息 的独立对象。这个转换能让你把请求方法参数化、延迟请求执行或把请求放在队列里&#xff0c;并且能实现可撤销操作。 问题 假如你正在开…...

ngx_cpystrn

定义在 src\core\ngx_string.c u_char * ngx_cpystrn(u_char *dst, u_char *src, size_t n) {if (n 0) {return dst;}while (--n) {*dst *src;if (*dst \0) {return dst;}dst;src;}*dst \0;return dst; } ngx_cpystrn 函数的作用是安全地将源字符串&#xff08;src&#x…...

常用的国内镜像源

常见的 pip 镜像源 阿里云镜像&#xff1a;https://mirrors.aliyun.com/pypi/simple/ 清华大学镜像&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 中国科学技术大学镜像&#xff1a;https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣镜像&#xff1a;https://pypi.doub…...

【小沐杂货铺】基于Three.JS绘制太阳系Solar System(GIS 、WebGL、vue、react)

&#x1f37a;三维数字地球系列相关文章如下&#x1f37a;&#xff1a;1【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;456:OpenGL、glfw、glut&#xff09;第一期2【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;456:OpenGL、glfw、glut&#xff09;第二期3【小沐…...

Navicat17详细安装教程(附最新版本安装包和补丁)2025最详细图文教程安装手册

目录 前言&#xff1a;为什么选择Navicat 17&#xff1f; 一、下载Navicat17安装包 二、安装Navicat 1.运行安装程序 2.启动安装 3.同意“协议” 4.设置安装位置 5.创建桌面图标 6.开始安装 7.安装完成 三、安装补丁 1.解押补丁包 2.在解压后的补丁包目录下找到“w…...

记忆宫殿APP:全方位脑力与思维训练,助你提升记忆力,预防老年痴呆

记忆宫殿APP&#xff0c;一款专业的记忆训练软件&#xff0c;能去帮你提升自己的记忆能力&#xff0c;多样的训练项目创新的记忆方法&#xff0c;全方面帮你去提升你的记忆能力。 记忆宫殿APP有丰富的记忆训练项目&#xff0c;如瞬间记忆、短时记忆、机械记忆等&#xff0c;以…...

SpringBoot+Spring+MyBatis相关知识点

目录 一、相关概念 1.spring框架 2.springcloud 3.SpringBoot项目 4.注解 5.SpringBoot的文件结构 6.启动类原理 二、相关操作 1.Jar方式打包 2.自定义返回的业务状态码 3.Jackson 4.加载配置文件 5.异常处理 三、优化配置 1.简化sql语句 2.查询操作 复杂查询 一…...

【力扣hot100题】(050)岛屿数量

一开始还以为会很难很难&#xff08;以为暴力搜索会时间超限要用别的办法&#xff09;&#xff0c;没想到并不难。 我最开始是用vector<vector<bool>>记录搜索过的地域&#xff0c;每次递归遍历周围所有地域。 class Solution { public:vector<vector<char…...

Opencv计算机视觉编程攻略-第九节 描述和匹配兴趣点

一般而言&#xff0c;如果一个物体在一幅图像中被检测到关键点&#xff0c;那么同一个物体在其他图像中也会检测到同一个关键点。图像匹配是关键点的常用功能之一&#xff0c;它的作用包括关联同一场景的两幅图像、检测图像中事物的发生地点等等。 1.局部模板匹配 凭单个像素就…...

pat学习笔记

two pointers 双指针 给定一个递增的正整数序列和一个正整数M&#xff0c;求序列中的两个不同位置的数a和b&#xff0c;使得它们的和恰好为M&#xff0c;输出所有满足条件的方案。例如给定序列{1,2,3,4,5,6}和正整数M 8&#xff0c;就存在268和358成立。 容易想到&#xff1…...

MoE Align Sort在医院AI医疗领域的前景分析(代码版)

MoE Align & Sort技术通过优化混合专家模型(MoE)的路由与计算流程,在医疗数据处理、模型推理效率及多模态任务协同中展现出显著优势,其技术价值与应用意义从以下三方面展开分析: 一、方向分析 1、提升医疗数据处理效率 在医疗场景中,多模态数据(如医学影像、文本…...

大数据概念介绍

这节课给大家讲一下大数据的生态架构, 大数据有很多的产品琳琅满目, 大家看到图上就知道产品很多, 那这些产品它们各自的功能是什么, 它们又是怎么样相互配合, 来完成一整套的数据存储, 包括分析计算这样的一些任务, 这节课就要给大家进行一个分析, 那我们按照数据处理…...

Linux(2025.3.15)

1、将/etc/passwd&#xff0c;/etc/shadow,/etc/group文件复制到/ceshi; 操作&#xff1a; &#xff08;1&#xff09;在根目录下创建ceshi目录&#xff1b; &#xff08;2&#xff09;复制&#xff1b; 结果&#xff1a; 2、找到/etc/ssh目录下ssh开头的所有文件并将其复制到…...

centosububntu设置开机自启动

一、centos 1.将脚本放到/etc/rc.d/init.d路径下 2.给脚本授权 sudo chmod -R 777 脚本名称 3.添加脚本至开机启动项中 sudo chkconfig --add 脚本名称 4.开启脚本 sudo chkconfig 脚本名称 on 5.查看开机启动项中是否包含该脚本 ls /etc/rc.d/rc*.d 二、ubuntu 1.在…...

基于大模型与动态接口调用的智能系统(知识库实现)

目录 引言 1、需求背景 2、实现原理 3、实现步骤 3.1 构建知识库接口调用提示模板 3.2 动态接口配置加载 3.3 智能参数提取链 3.4 接口智能路由 3.5 建议生成链 3.6 组合完整工作流 3.7 展示效果 总结 引言 在医疗信息化快速发展的今天,我们开发了一个智能问诊系…...

23种设计模式-行为型模式-迭代器

文章目录 简介问题解决代码设计关键点&#xff1a; 总结 简介 迭代器是一种行为设计模式&#xff0c;让你能在不暴露集合底层表现形式(列表、栈和树等)的情况下遍历集合中所有的元素。 问题 集合是编程中最常使用的数据类型之一。 大部分集合使用简单列表存储元素。但有些集…...

【Java集合】ArrayList源码深度分析

参考笔记&#xff1a; java ArrayList源码分析&#xff08;深度讲解&#xff09;-CSDN博客 【源码篇】ArrayList源码解析-CSDN博客 目录 1.前言 2. ArrayList简介 3. ArrayList类的底层实现 4. ArrayList的源码Debug 4.1 使用空参构造 &#xff08;1&#xff09;开始De…...

ISIS单区域抓包分析

一、通用头部报文 Intra Domain Routing Protocol Discriminator&#xff1a;域内路由选择协议鉴别符&#xff1a;这里是ISIS System ID Length&#xff1a;NSAP地址或NET中System ID区域的长度。值为0时&#xff0c;表示System ID区域的长度为6字节。值为255时&#xff0c;表…...

关键业务数据如何保持一致?主数据管理的最佳实践!

随着业务规模的扩大和系统复杂性的增加&#xff0c;如何确保关键业务数据的一致性成为许多企业面临的重大挑战。数据不一致可能导致决策失误、运营效率低下&#xff0c;甚至影响客户体验。因此&#xff0c;主数据管理&#xff08;Master Data Management&#xff0c;简称MDM&am…...

ISIS单区域配置

一、什么是ISIS单区域 ISIS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;单区域是指使用ISIS路由协议时&#xff0c;所有路由器都位于同一个区域&#xff08;Area&#xff09;内的网络配置。 二、实验拓扑 三、实验目的…...

Visual Basic语言的物联网

Visual Basic语言在物联网中的应用 引言 物联网&#xff08;IoT&#xff09;作为一种新兴的技术趋势&#xff0c;正在深刻地改变我们的生活方式与工业制造过程。在众多编程语言中&#xff0c;Visual Basic&#xff08;VB&#xff09;凭借其简单易用的特性&#xff0c;逐渐成为…...

【小沐杂货铺】基于Three.JS绘制三维数字地球Earth(GIS 、WebGL、vue、react)

&#x1f37a;三维数字地球系列相关文章如下&#x1f37a;&#xff1a;1【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;456:OpenGL、glfw、glut&#xff09;第一期2【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;456:OpenGL、glfw、glut&#xff09;第二期3【小沐…...