Only copy UriBuilderFactory when customized

This commit ensures that, when creating a RestClient.Builder from a
RestTemplate, the UriBuilderFactory is only copied if it has been
changed from the default values.
Before this commit, the UriBuilderFactory was effectively alway copied,
resulting in not being able to use a baseUrl.

This commit also introduces a small memory optimization in
DefaultUriBuilderFactory, so that default environment variables are
created lazily.

Closes gh-32180
This commit is contained in:
Arjen Poutsma
2024-02-02 15:11:46 +01:00
parent 7025b7aac2
commit e15c150696
3 changed files with 85 additions and 18 deletions

View File

@@ -54,6 +54,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriTemplateHandler;
/**
* Default implementation of {@link RestClient.Builder}.
@@ -172,9 +173,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
public DefaultRestClientBuilder(RestTemplate restTemplate) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
if (restTemplate.getUriTemplateHandler() instanceof UriBuilderFactory builderFactory) {
this.uriBuilderFactory = builderFactory;
}
this.uriBuilderFactory = getUriBuilderFactory(restTemplate);
this.statusHandlers = new ArrayList<>();
this.statusHandlers.add(StatusHandler.fromErrorHandler(restTemplate.getErrorHandler()));
@@ -191,6 +190,26 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.observationConvention = restTemplate.getObservationConvention();
}
@Nullable
private static UriBuilderFactory getUriBuilderFactory(RestTemplate restTemplate) {
UriTemplateHandler uriTemplateHandler = restTemplate.getUriTemplateHandler();
if (uriTemplateHandler instanceof DefaultUriBuilderFactory builderFactory) {
// only reuse the DefaultUriBuilderFactory if it has been customized
if (builderFactory.hasRestTemplateDefaults()) {
return null;
}
else {
return builderFactory;
}
}
else if (uriTemplateHandler instanceof UriBuilderFactory builderFactory) {
return builderFactory;
}
else {
return null;
}
}
private static ClientHttpRequestFactory getRequestFactory(RestTemplate restTemplate) {
ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();
if (requestFactory instanceof InterceptingClientHttpRequestFactory interceptingClientHttpRequestFactory) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Optional;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -46,7 +47,8 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
private EncodingMode encodingMode = EncodingMode.TEMPLATE_AND_VALUES;
private final Map<String, Object> defaultUriVariables = new HashMap<>();
@Nullable
private Map<String, Object> defaultUriVariables;
private boolean parsePath = true;
@@ -108,9 +110,18 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
* @param defaultUriVariables default URI variable values
*/
public void setDefaultUriVariables(@Nullable Map<String, ?> defaultUriVariables) {
this.defaultUriVariables.clear();
if (defaultUriVariables != null) {
this.defaultUriVariables.putAll(defaultUriVariables);
if (this.defaultUriVariables == null) {
this.defaultUriVariables = new HashMap<>(defaultUriVariables);
}
else {
this.defaultUriVariables.putAll(defaultUriVariables);
}
}
else {
if (this.defaultUriVariables != null) {
this.defaultUriVariables.clear();
}
}
}
@@ -118,7 +129,12 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
* Return the configured default URI variable values.
*/
public Map<String, ?> getDefaultUriVariables() {
return Collections.unmodifiableMap(this.defaultUriVariables);
if (this.defaultUriVariables != null) {
return Collections.unmodifiableMap(this.defaultUriVariables);
}
else {
return Collections.emptyMap();
}
}
/**
@@ -141,6 +157,20 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
return this.parsePath;
}
/**
* Indicates whether this {@code DefaultUriBuilderFactory} uses the default
* {@link org.springframework.web.client.RestTemplate RestTemplate}
* settings.
* @since 6.1.4
*/
public boolean hasRestTemplateDefaults() {
// see RestTemplate::initUriTemplateHandler
return this.baseUri == null &&
this.encodingMode == EncodingMode.URI_COMPONENT &&
CollectionUtils.isEmpty(this.defaultUriVariables) &&
this.parsePath;
}
// UriTemplateHandler
@@ -379,8 +409,8 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
@Override
public URI build(Map<String, ?> uriVars) {
if (!defaultUriVariables.isEmpty()) {
Map<String, Object> map = new HashMap<>();
if (!CollectionUtils.isEmpty(defaultUriVariables)) {
Map<String, Object> map = new HashMap<>(defaultUriVariables.size() + uriVars.size());
map.putAll(defaultUriVariables);
map.putAll(uriVars);
uriVars = map;
@@ -394,7 +424,7 @@ public class DefaultUriBuilderFactory implements UriBuilderFactory {
@Override
public URI build(Object... uriVars) {
if (ObjectUtils.isEmpty(uriVars) && !defaultUriVariables.isEmpty()) {
if (ObjectUtils.isEmpty(uriVars) && !CollectionUtils.isEmpty(defaultUriVariables)) {
return build(Collections.emptyMap());
}
if (encodingMode.equals(EncodingMode.VALUES_ONLY)) {