使用Spring RestTemplate的REST客户端最佳实践

17

我已经阅读了一些有关在使用SPRING管理Beans的Java Web应用程序中实现REST客户端的教程。

我发现每个例子每次执行REST请求时都会创建一个新的RestTemplate。

通常Web应用程序使用单例Spring Bean。

因此,我想知道在Spring配置应用程序时使用RestTemplate的最佳实践是什么?
使用单例RestTemplate?
还是在每个请求中创建新的RestTemplate?

请给出建议并描述所有情况。

2个回答

18
创建一个Bean,它将返回RestTemplate并在需要的任何类中自动装配它,是实现此目标的最佳方法之一。
像这样:
@Configuration
public class ProductServiceConfig {

    @Value("${product.username}")
    private String productServiceUsername;

    @Value("${product.password}")
    private String productServicePassword;

    @Bean(name = "restTemplateForProductService")
    public RestTemplate prepareRestTemplateForProductService() {

        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(productServiceUsername, productServicePassword));

        RequestConfig.Builder requestBuilder = RequestConfig.custom();
        requestBuilder = requestBuilder.setConnectTimeout(1000);

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
        httpClientBuilder.setDefaultRequestConfig(requestBuilder.build());
        CloseableHttpClient httpClient = httpClientBuilder.build();

        HttpComponentsClientHttpRequestFactory rf = new HttpComponentsClientHttpRequestFactory(httpClient);

        return new RestTemplate(rf);
    }
}

这样,您可以设置REST调用所需的各种参数,如超时或凭据等。当您想要使用时,只需执行

@Autowired
RestTemplate restTemplateForProductService;

restTemplateForProductService.......

相对于使用new RestTemplate()的另一个优势是,如果您需要通过REST调用不同的服务,则可以定义多个bean(具有不同的配置),返回RestTemplates并使用名称进行自动装配。


我们能否将上述RestTemplate bean的创建变成通用的,比如说我们有一个bean,根据环境生成两种不同类型的模板。因此,我们可以重构一下,而不是为ClientA和ClientB分别创建rest template,这两个客户端几乎所有配置都相同,只是url不同。我们能否进行某种重构?https://stackoverflow.com/q/63117202/3560140 - NeverGiveUp161

2
当使用RestTemplate时,您应该采用以下其中一种方法:
静态实例:
/**
 * Rest template client
 */
private static final RestTemplate TEMPLATE = new RestTemplate(new RestClientRequestFactory());


static{
    //Set your options here
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.featuresToEnable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);

    builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    builder.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

    MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
    jsonMessageConverter.setObjectMapper(builder.build());

    TEMPLATE.setMessageConverters(Arrays.asList(jsonMessageConverter));
}

或者在@Configuration类中声明一个Spring Bean:

@Bean
public RestTemplate restTemplate(){
    RestTemplate template = new RestTemplate(new RestClientRequestFactory())
            //customize
    return template;
}

如果您查看RestTemplate类,则应避免过多地调用默认构造函数:

public RestTemplate() {
    this.messageConverters = new ArrayList();
    this.errorHandler = new DefaultResponseErrorHandler();
    this.uriTemplateHandler = new DefaultUriTemplateHandler();
    this.headersExtractor = new RestTemplate.HeadersExtractor();
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(new StringHttpMessageConverter());
    this.messageConverters.add(new ResourceHttpMessageConverter());
    this.messageConverters.add(new SourceHttpMessageConverter());
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    if (romePresent) {
        this.messageConverters.add(new AtomFeedHttpMessageConverter());
        this.messageConverters.add(new RssChannelHttpMessageConverter());
    }

    if (jackson2XmlPresent) {
        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
    } else if (jaxb2Present) {
        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }

    if (jackson2Present) {
        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    } else if (gsonPresent) {
        this.messageConverters.add(new GsonHttpMessageConverter());
    }

}

由于请求量巨大,我的应用程序出现了“打开文件太多”的问题。许多转换器加载存在问题。我能否为多个模板重复使用一个转换器列表?它是线程安全的吗? - demon101
1
是的,RestTemplate是线程安全的。https://spring.io/blog/2009/03/27/rest-in-spring-3-resttemplate - demon101

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接