Spring Boot RestClient 作为单例或根据请求创建新实例

问题描述 投票:0回答:1

查看文档here,用于

RestClient
的预期模式是自动装配
RestClient.Builder
,然后在服务的构造函数方法中构建
RestClient
。虽然这在常规课程中使用时效果很好,但在测试时效果不佳。

根据文档here,当应用程序中仅使用 1 个构建器时,

@RestClientTest
@AutoConfigureMockRestServiceServer
可用。随着创建更多实例并且需要特定的自定义,很明显您将希望为每一项特定服务拥有多个
RestClient.Builder

有了这个,我能看到配置它的唯一方法是将

MockRestServiceServer
绑定到每个
RestClient.Builder
使得将它们作为 beans 似乎是显而易见的路线。现在您可以拥有如下代码:

配置逻辑:

@RequiredArgsConstructor
@Configuration
public class RestClientConfiguration {
    private final RestClientBuilderConfigurer restClientBuilderConfigurer;

    @Bean
    RestClient.Builder microservice1RestClientBuilder() {
        return createDefaultRestClientBuilder()
                .baseUrl("http://localhost:8081");
    }

    /**
     * Copied from
     * {@link org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
     * RestClientAutoConfiguration}
     * 
     * @return
     */
    private RestClient.Builder createDefaultRestClientBuilder() {
        RestClient.Builder builder = RestClient
                .builder()
                .requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS));
        return restClientBuilderConfigurer.configure(builder);
    }
}

控制器逻辑:

@RequiredArgsConstructor
@RestController
public class MainController {
    private final MainService mainService;

    @GetMapping
    public String hello() {
        return mainService.helloService();
    }
}

服务逻辑:

@Service
public class MainService {
    private final RestClient restClient;
    
    public MainService(RestClient.Builder microservice1RestClientBuilder) {
        super();
        restClient = microservice1RestClientBuilder.build();
    }

    public String helloService() {
        return restClient.get().uri("/").retrieve().body(String.class);
    }
}

测试逻辑:

@SpringBootTest
@AutoConfigureMockMvc
class MainControllerTest {
    private MockRestServiceServer microservice1Server;

    @Autowired
    private RestClient.Builder microservice1RestClientBuilder;

    @Autowired
    private MockMvc mvc;

    @BeforeEach
    void init() {
        microservice1Server = MockRestServiceServer.bindTo(microservice1RestClientBuilder).build();
    }

    @Test
    void test() throws Exception {
        microservice1Server
                .expect(requestTo("http://localhost:8081/"))
                .andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN));
        mvc.perform(get("/")).andExpect(status().is2xxSuccessful()).andExpect(content().string("Hello world"));
    }
}

这一切看起来应该可以工作,但事实并非如此,因为在创建

RestClient
之前测试不会绑定。要解决此问题,可以将服务类更改为以下内容,测试将开始工作。

@RequiredArgsConstructor
@Service
public class MainService {
    private final RestClient.Builder microservice1RestClientBuilder;

    public String helloService() {
        return microservice1RestClientBuilder.build().get().uri("/").retrieve().body(String.class);
    }
}

测试用例现在可以工作,但是现在每次调用都会创建

RestClient
,这感觉没有必要。这让我相信要么我错过了它的预期使用方式,要么在某个地方存在差距。我对这个问题的希望是展示使用
RestClient
的最佳方法,不仅用于一般用途,而且还用于测试目的。

spring spring-boot spring-mvc spring-boot-test spring-restclient
1个回答
0
投票

你是对的,

@AutoConfigureMockMvc
在这种情况下不起作用。看起来使用
MockServerRestClientCustomizer
会带来最好的运气。该类的 javadoc 显示了其用法示例,并且有 集成测试用例 也可以用作示例。

使用这种技术,你的测试类可能看起来像这样:

@SpringBootTest
class MainControllerTest {

    @Autowired
    private MainService mainService;

    @Autowired
    private MockServerRestClientCustomizer customizer;

    @Autowired
    private MockMvc mvc;

    @Test
    void test() throws Exception {
        this.customizer.getServer(this.mainService.getRestClientBuilder())
                .expect(requestTo("http://localhost:8081/"))
                .andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN));

        mvc.perform(get("/"))
            .andExpect(status().is2xxSuccessful())
            .andExpect(content().string("Hello world"));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.