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

HttpClient常用操作及封装工具类 HttpClient 是一个用于发送 HTTP 请求和接收 HTTP 响应的类库/模块,可以用于编写客户端应用程序,通过 HTTP 与服务器进行通信。 HttpClient 提供了一组方法和类,使得发送 HTTP 请求变得简单和方便。它支持各种 HTTP 方法,例如 GET、POST、PUT、DELETE 等,并可以设置请求的头部信息、请求参数、请求体等。 阅读详情
AI摘要

本文介绍了Spring提供的四种HTTP客户端。包括同步的RestTemplate,虽简单但并发性能低;支持流式调用的RestClient,也是同步客户端;非阻塞响应式的WebClient,用于替代RestTemplate;还有基于动态代理的声明式接口Http Interface。文中还提及各客户端的使用、配置、异常处理等内容。

目录

常见的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 symbol
  symbol:   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 {
    @Bean
    public 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 完成构造器注入后再进行初始化
    @PostConstruct
    public 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)
	@ConditionalOnMissingBean
	RestClient.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)
	@ConditionalOnMissingBean
	public 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 {
    @Bean
    public 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 {

    @Bean
    public 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的调用方式。

Tomcat 7.0.69 版本变更解析:HTTP请求头非法字符校验的3个关键影响 本文深入解析Tomcat 7.0.69版本对HTTP请求头非法字符的严格校验机制,探讨其对WebSocket连接、反向代理配置及非标准客户端的3个关键影响。通过源码级解决方案和长期架构建议,帮助开发者有效应对升级后的兼容性问题,确保系统安全稳定运行。 阅读详情

相关推荐

稳住GLM-5.1-cc长连接:SSE协议在大模型流式推理中的稳定性实践

SSE(Server-Sent Events)是一种基于HTTP的轻量级服务端推送协议,广泛用于大模型流式响应场景。其核心原理是通过单向持久化HTTP连接实现低延迟、高吞吐的数据推送,但缺乏内置心跳、重连与状态同步机制,导致在GLM-5.1-cc等长程推理任务中极易出现上下文丢失、连接静默中断与内存泄漏等问题。该技术的价值在于平衡WebSocket的复杂性与轮询的低效性,特别适配reasoning_content与content分阶段输出的AI推理范式。典型应用场景包括智能体开发、代码生成、多步Linux系

ama7449的博客 398

SpringBoot使用HttpClient

一、HttpClient 两个系统间如何互相访问?两个tomcat上的项目如何互相访问 采用HttpClient实现跨系统的接口调用。 1 1.2、 什么是HttpClient 官网:http://hc.apache.org/index.html 特点: HttpClient别名:HttpComponents HttpClient可以发送get、post、put、delete、…等请求 1.3、 HttpClient入门案例 <dependency> <groupId&..

weixin_57763462的博客 8046

Spring Boot进阶(72):【教程】用Spring Boot和HttpClient实现高效的HTTP请求

本文主要包括以下内容: 使用Spring Boot和HttpClient实现基础的HTTP请求 实现带有请求头和请求体的HTTP请求 实现HTTPS请求 借助于Apache HttpClient的高级特性,如连接池、请求重试、请求超时等 通过单元测试验证HTTP请求的正确性和可靠性

**My Coding Family** 5914

【HttpClient】在 SpringBoot 中使用 HttpClient 实现 HTTP 请求

越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。 HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。 ...

sco5282的博客 1万+

Spring Boot入门教程(五十): httpclient

引入依赖 &amp;amp;lt;dependency&amp;amp;gt; &amp;amp;lt;groupId&amp;amp;gt;org.apache.httpcomponents&amp;amp;lt;/groupId&amp;amp;gt; &amp;amp;lt;artifactId&amp;amp;gt;httpclient&amp;amp;lt;/artifactId&amp;amp;g

人不风流枉少年 4491

嵌入式开发必备:手把手教你用DB-9连接器搞定RS-232串口通信(附万用表测试技巧)

本文详细解析了嵌入式开发中RS-232串口通信的硬件连接核心。从DB-9连接器的引脚定义入手,深入讲解DTE与DCE设备角色,明确直连线与交叉线的选择法则,并重点分享了使用万用表进行线缆连通性测试、电压测量及故障诊断的实战技巧,帮助开发者彻底掌握串口通信的硬件连接与排查方法。

tgb345678的博客 1164

Java+Vue+Spring Boot+MySQL全栈汽车资讯网站毕业设计实战指南

在Web应用开发领域,前后端分离架构已成为主流范式,其核心在于通过清晰的职责划分提升开发效率和系统可维护性。其基本原理是前端负责用户界面与交互逻辑,后端专注于业务处理与数据管理,两者通过RESTful API进行通信。这种架构的技术价值在于实现了技术栈解耦、团队并行开发以及更好的可扩展性,广泛应用于内容管理系统、电商平台及企业级应用。对于计算机专业学生而言,掌握一个涵盖用户认证、内容管理(CRUD)、数据展示等核心模块的全栈项目,是理解现代软件开发流程、完成毕业设计的关键。本文以汽车资讯网站这一经典应用场景

weixin_34167819的博客 1521

LabVIew串口收发的一些问题

LabView串口收发实验总结

weixin_45508826的博客 2065

(课程设计)基于Spring Boot的社区生鲜配送管理系统的设计与实现-计算机毕设 附源码08610

注册用户功能分析: 登录注册:注册用户可以通过手机号、邮箱等信息完成账户的注册和登录。登录后,系统会根据用户的角色和权限展示相应的功能模块,保证了用户体验的个性化。 首页:为用户展示系统的核心内容,包括热销生鲜商品、促销活动、最新消息等。通过首页,用户能够快速了解商城动态,便于后续购物决策。 通知公告:系统通过通知公告模块向用户发布相关的系统信息、活动信息、服务更新等,确保用户能够及时了解平台的最新消息和变动。 新闻资讯:用户可以在平台上查看各类生鲜相关的新闻资讯,包括饮食健康、食材选择等内容,为用户提供有

ID3461074420的博客 3834

从“Hello World”到区域赛银牌:我的ACM算法打怪升级全记录(附各阶段工具包)

本文记录了作者从编程新手到ACM区域赛银牌获得者的完整学习历程,详细分享了各阶段的算法学习路线、刷题策略和实战经验。涵盖从基础算法到动态规划的系统训练方法,并附赠经过竞赛检验的工具包和资源清单,为算法竞赛爱好者提供实用参考。

weixin_29201859的博客 121

Docker容器化部署Ceph集群:从零搭建高可用分布式存储

本文详细介绍了如何使用Docker容器化部署Ceph集群,从零搭建高可用分布式存储系统。通过环境一致性、快速扩容和资源隔离等优势,Docker显著提升了Ceph集群的部署效率和运维便捷性。文章涵盖硬件规划、镜像选择、单节点部署、多节点扩展及性能调优等实战内容,帮助开发者快速掌握分布式存储系统的容器化部署技巧。

weixin_29081101的博客 422

009 java毕设&课程设计——约局 · 剧本杀约局管理平台

摘要: 约局·剧本杀管理平台是一个单商家多客户模式的在线约局系统,采用Spring Boot+Vue3技术栈。客户可浏览剧本、发起/加入场次、发布评测;商家能管理剧本场次、审核评价并查看统计数据。系统使用JWT认证,提供默认管理员、商家和客户账号。后端基于Spring Boot+MyBatis-Plus,前端使用Vue3+Element Plus,支持剧本上下架、场次人数动态调整等业务逻辑,数据库初始化脚本自动生成演示数据。项目结构清晰,包含详细的接口约定和业务设计说明,适合剧本杀商家快速搭建线上约局平台。

439

3、★☛基于STM32的手机通过wifi控LED灯√♠★

3、基于STM32的手机通过wifi控LED灯 文章目录3、基于STM32的手机通过wifi控LED灯1、Introduce介绍1.1、课题背景及其意义1.2、 国内外的研究状况2、方案的设计2.1、控制方案的确定2.2、控制方式的选择2.2.1、 单片机芯片的选择方案一方案二方案三方案四2.2.2、无线遥控模块的选择方案一方案二方案三3、硬件电路的设计3.1、系统的功能分析及体系结构设计3.1.1、系统功能分析3.1.2、系统总体结构3.2、模块电路的设计3.2.1、 STM32单片机核心电路设计3.2.

行走的皮卡丘 2960

【金仓数据库征文】从“能下单”到“不超卖”:Spring Boot + KingbaseES 事务订单接口实战

本文记录了一次基于Spring Boot和KingbaseES的本地模拟实验,重点验证订单系统中的事务处理、幂等控制和并发扣库存功能。实验环境采用Spring Boot 3.3.5、Spring JDBC与KingbaseES V9数据库,通过五张核心表(商品、库存、订单、订单明细和库存流水)构建完整业务流程。实验过程中发现初始库存设置偏差问题,强调了数据库状态验证的重要性。文章详细展示了正常下单、事务回滚、请求幂等和并发扣减等场景的验证步骤,并通过SQL查询和接口返回的交叉验证确保结果可靠性。

月亮永悬不落,剑指巅峰 1万+

由Sensor光谱响应曲线联想到的白平衡增益计算

由Sensor光谱响应曲线联想到的白平衡增益计算 由于传感器硬件的限制,传感器在某个预定波长范围内的响应不可能是100% 响应。现实中都是类似于正态分布的单峰函数。如果仅做粗略计算,由一个中心波长和半高宽(也成半波宽)完全可以由数学公式计算出一个高斯分布曲线。这个分布曲线就叫波谱响应函数。 波谱响应函数反映了传感器通道获取能量的情况。实际上每个传感器厂商都提供了具体的光谱响应函数,一般由两列数据组成,一列数据是波长,另一列数据是相对响应强度。下图某sensor光谱的响应的波谱响应函数。 ...

大熊 7487

把 SAP OData 查询模式讲透:从 $select 到 $expand 的设计、实现与性能边界

摘要:本文深入探讨SAP生态中OData查询模式的核心设计与实现要点。文章指出,OData不仅是接口协议,更是HTTP、REST与数据模型的统一语言,其性能关键取决于查询选项($select、$filter等)的设计与实现质量。重点分析了$select的投影优化、$orderby的排序语义、$filter的数据库下推策略,以及$count的高效计数方法。通过具体示例和最佳实践,强调应将查询逻辑尽量下推至数据库层,避免在ABAP应用层处理大数据集,从而提升服务性能、降低资源消耗,实现Clean Core架构理

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 647

http工具_springboot项目优雅的http客户端工具,真香!

★★★建议星标我们★★★公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。2020年Java原创面试题库连载中【000期】Java最全面试题库思维导图【001期】JavaSE面试题(一):面向对象【002期】JavaSE面试题(二):基本数据类型与访问修饰符【003期】JavaSE面试题(三):Java...

weixin_39606799的博客 266

点云语义分割:PointNet训练S3DIS数据集

文章目录一、数据准备1.1、数据下载二、训练三、测试 项目地址:pointnet 此次我们是用pointnet网络来做语义分割。代码在pointnet项目中的sem_seg文件夹下,里面也有README.md介绍了训练的过程。 一、数据准备 1.1、数据下载 原始数据下载 处理好的h5格式数据下载 二、训练 三、测试 ...

zhaoguanhua的博客 4176

走gateway接口偶现返回Internal Server Error

spring.cloud.gateway.httpclient.pool.maxIdleTime 这个配置项用于配置 Spring Cloud Gateway 中底层使用的 HttpClient 连接池中,连接的最大空闲时间。修改点:spring.cloud.gateway.httpclient.pool.maxIdleTime<restTemplate.config.connection.connectTimeout。如果一个连接在连接池中空闲时间超过了这个配置的时间,连接将被关闭并从连接池中移除。

m0_75203563的博客 1119

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java学习专栏!全网最牛!

00254:《SpringCloud Alibaba微服务实战十四 - SpringCloud Gateway集成Oauth2.0》00242:《SpringCloud Alibaba微服务实战二十四 - SpringCloud Gateway的全局异常处理》00251:《SpringCloud Alibaba微服务实战十五 - SpringCloud 容器化部署》00248:《SpringCloud Alibaba微服务实战十八 - Oauth2.0 自定义授权模式》00184:《TCP网络那点破事!..

weixin_70730532的博客 3786

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

008-SpringCloud系列之Gateway新一代网关

文章目录基本介绍三大核心概念Gateway工作流程入门配置(使用绝对路径)通过微服务名实现动态路由Predicate的使用Filter的使用常用的GatewayFilter自定义过滤器 涉及代码地址:https://gitee.com/codinginn/SpringCloudTurorial/tree/master/SpringCloudModules 思维导图和相关安装包:https://pan.baidu.com/s/1ogGF8sQrMxYc4HrTmjh9mA?pwd=aaaa 提取码:aaaa

Learning_xzj的博客 635

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

(课程设计)基于Java的汽车驾校管理系统设计与实现-计算机毕设 附源码 77267

本系统的总体功能设计包括学员用户、教练用户和管理员三个角色的管理需求,旨在实现基于Java的汽车驾校管理系统的高效管理与资源优化配置。对于学员用户,系统提供登录注册、首页、网站公告、驾校资讯、反馈建议、课程信息、我的账户、个人中心(个人首页、学车报名、课程预约、排课信息、学习进度、考试预约、考试结果、毕业信息、反馈建议、收藏、评论管理)等功能。教练用户端则包括登录、首页、网站公告、驾校资讯、反馈建议、课程信息、我的账户、个人中心(个人首页、学车报名、车辆分配、车辆报修、课程信息、课程预约、排课信息、学习进度

ID3461074420的博客 229

五子棋在线对战平台测试报告

本测试项目针对五子棋在线对战平台(GobangGame)开展。系统提供完整的"注册 → 登录 → 匹配 → 对局 → 积分结算 → 战绩查询"全流程服务,前端为 4 个静态页面(login.htmllobby.htmlroom.html),后端采用 Spring Boot + MyBatis-Plus + MySQL + Redis + RabbitMQ,基于 STOMP/WebSocket 实现实时对局推送。术语含义前端与后端建立的实时双向连接(STOMP 协议)匹配。

qq_43209299的博客 217

工作中使用到的单词(软件开发)

1.エイリアス   alias 别名 powershell使用 get-help 命令时,会得到与此命令 相关(别名)的命令 2.プロキシ proxy 代 理 3.プロモート   promote 促进,提升   开发的系统新功能上线发布,有时会使用到这个词,比如 STプロモート 还有使用リリース这个词的。比如STリリース。个人认为两者含义相同。 4.見逃し み................................................................

sun0322 6万+

【课程设计】基于Vue的驾校车辆管理系统 计算机毕业设计源码16480

本系统的总体功能设计包括普通用户、教练用户和管理员三个角色的管理需求,旨在实现基于vue的汽车驾校车辆管理系统的高效管理与资源优化配置。对于普通用户,系统提供登录注册、首页、交流论坛、活动公告、驾校资讯、留言反馈、驾校信息、我的账户、个人中心(报名信息、教练安排、收藏、评论管理)等功能。教练用户端则包括登录、教练安排管理、车辆信息管理、安全事件管理、维修保养管理、维修进度管理等功能。维修工用户则包括登录、维修保养管理、维修进度管理、配件仓库管理、配件出库管理、维修采购管理。管理员则具备更为全面的管理功能..

高级程序源的博客 655

springboot希腊神话推荐系统98276-计算机课程设计/毕业设计

管理员可对系统信息进行全面管理,包括用户管理、神话人物管理、故事篇章管理、评论管理、公告资讯管理、系统数据统计等。管理员可以对所有内容进行增删改查操作,审核用户评论,维护系统正常运行等

Biye_Design的博客 423
上一篇: 学透Spring Boot — 008. 外部配置文件和目录
下一篇: 学透Spring Boot — 010. 单元测试和Spring Test
postnull咖啡
博客等级 码龄10年 1026粉丝 59原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值