Add Builder to HttpServiceProxyFactory

HttpServiceProxyFactory now support programmatic initialization through
a builder, while bean-style initialization is deprecated.

See gh-29296
This commit is contained in:
rstoyanchev 2022-10-17 12:23:42 +01:00
parent e03abc94ff
commit 5cb3af708c
14 changed files with 413 additions and 107 deletions

View File

@ -43,10 +43,10 @@ import org.springframework.util.StringValueResolver;
import org.springframework.web.service.annotation.HttpExchange; import org.springframework.web.service.annotation.HttpExchange;
/** /**
* Factory for creating a client proxy given an HTTP service interface with * Factory to create a client proxy from an HTTP service interface with
* {@link HttpExchange @HttpExchange} methods. * {@link HttpExchange @HttpExchange} methods.
* *
* <p>This class is intended to be declared as a bean in a Spring configuration. * <p>To create an instance, use static methods to obtain a {@link Builder Builder}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 6.0 * @since 6.0
@ -54,81 +54,98 @@ import org.springframework.web.service.annotation.HttpExchange;
*/ */
public final class HttpServiceProxyFactory implements InitializingBean, EmbeddedValueResolverAware { public final class HttpServiceProxyFactory implements InitializingBean, EmbeddedValueResolverAware {
private final HttpClientAdapter clientAdapter; @Nullable
private final BuilderInitializedFactory builderInitializedFactory;
@Nullable @Nullable
private List<HttpServiceArgumentResolver> customArgumentResolvers; private final BeanStyleFactory beanStyleFactory;
@Nullable
private List<HttpServiceArgumentResolver> argumentResolvers;
@Nullable
private ConversionService conversionService;
@Nullable
private StringValueResolver embeddedValueResolver;
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
private Duration blockTimeout = Duration.ofSeconds(5);
/** /**
* Create an instance with the underlying HTTP client to use. * Create an instance with the underlying HTTP client to use.
* @param clientAdapter an adapter for the client * @param clientAdapter an adapter for the client
* @see org.springframework.web.reactive.function.client.support.WebClientAdapter#createHttpServiceProxyFactory(org.springframework.web.reactive.function.client.WebClient) * @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* the HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public HttpServiceProxyFactory(HttpClientAdapter clientAdapter) { public HttpServiceProxyFactory(HttpClientAdapter clientAdapter) {
Assert.notNull(clientAdapter, "HttpClientAdapter is required"); this.beanStyleFactory = new BeanStyleFactory(clientAdapter);
this.clientAdapter = clientAdapter; this.builderInitializedFactory = null;
}
private HttpServiceProxyFactory(
HttpClientAdapter clientAdapter, List<HttpServiceArgumentResolver> argumentResolvers,
@Nullable StringValueResolver embeddedValueResolver,
ReactiveAdapterRegistry reactiveAdapterRegistry, Duration blockTimeout) {
this.beanStyleFactory = null;
this.builderInitializedFactory = new BuilderInitializedFactory(
clientAdapter, argumentResolvers, embeddedValueResolver, reactiveAdapterRegistry, blockTimeout);
} }
/** /**
* Register a custom argument resolver, invoked ahead of default resolvers. * Register a custom argument resolver, invoked ahead of default resolvers.
* @param resolver the resolver to add * @param resolver the resolver to add
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* the HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public void addCustomArgumentResolver(HttpServiceArgumentResolver resolver) { public void addCustomArgumentResolver(HttpServiceArgumentResolver resolver) {
if (this.customArgumentResolvers == null) { Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.customArgumentResolvers = new ArrayList<>(); this.beanStyleFactory.addCustomArgumentResolver(resolver);
}
this.customArgumentResolvers.add(resolver);
} }
/** /**
* Set the custom argument resolvers to use, ahead of default resolvers. * Set the custom argument resolvers to use, ahead of default resolvers.
* @param resolvers the resolvers to use * @param resolvers the resolvers to use
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* the HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public void setCustomArgumentResolvers(List<HttpServiceArgumentResolver> resolvers) { public void setCustomArgumentResolvers(List<HttpServiceArgumentResolver> resolvers) {
this.customArgumentResolvers = new ArrayList<>(resolvers); Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.beanStyleFactory.setCustomArgumentResolvers(resolvers);
} }
/** /**
* Set the {@link ConversionService} to use where input values need to * Set the {@link ConversionService} to use where input values need to
* be formatted as Strings. * be formatted as Strings.
* <p>By default this is {@link DefaultFormattingConversionService}. * <p>By default this is {@link DefaultFormattingConversionService}.
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* the HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public void setConversionService(ConversionService conversionService) { public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService; Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.beanStyleFactory.setConversionService(conversionService);
} }
/** /**
* Set the StringValueResolver to use for resolving placeholders and * Set the StringValueResolver to use for resolving placeholders and
* expressions in {@link HttpExchange#url()}. * expressions in {@link HttpExchange#url()}.
* @param resolver the resolver to use * @param resolver the resolver to use
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* an HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
@Override @Override
public void setEmbeddedValueResolver(StringValueResolver resolver) { public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver; Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.beanStyleFactory.setEmbeddedValueResolver(resolver);
} }
/** /**
* Set the {@link ReactiveAdapterRegistry} to use to support different * Set the {@link ReactiveAdapterRegistry} to use to support different
* asynchronous types for HTTP service method return values. * asynchronous types for HTTP service method return values.
* <p>By default this is {@link ReactiveAdapterRegistry#getSharedInstance()}. * <p>By default this is {@link ReactiveAdapterRegistry#getSharedInstance()}.
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* an HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) { public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) {
this.reactiveAdapterRegistry = registry; Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.beanStyleFactory.setReactiveAdapterRegistry(registry);
} }
/** /**
@ -136,42 +153,22 @@ public final class HttpServiceProxyFactory implements InitializingBean, Embedded
* with a synchronous (blocking) method signature. * with a synchronous (blocking) method signature.
* <p>By default this is 5 seconds. * <p>By default this is 5 seconds.
* @param blockTimeout the timeout value * @param blockTimeout the timeout value
* @deprecated as of 6.0 RC1 in favor of using the Builder to initialize
* an HttpServiceProxyFactory instance.
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public void setBlockTimeout(Duration blockTimeout) { public void setBlockTimeout(Duration blockTimeout) {
this.blockTimeout = blockTimeout; Assert.state(this.beanStyleFactory != null, "HttpServiceProxyFactory was created through the builder");
this.beanStyleFactory.setBlockTimeout(blockTimeout);
} }
@Override @Override
@Deprecated
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
if (this.beanStyleFactory != null) {
this.conversionService = (this.conversionService != null ? this.beanStyleFactory.afterPropertiesSet();
this.conversionService : new DefaultFormattingConversionService());
this.argumentResolvers = initArgumentResolvers(this.conversionService);
}
private List<HttpServiceArgumentResolver> initArgumentResolvers(ConversionService conversionService) {
List<HttpServiceArgumentResolver> resolvers = new ArrayList<>();
// Custom
if (this.customArgumentResolvers != null) {
resolvers.addAll(this.customArgumentResolvers);
} }
// Annotation-based
resolvers.add(new RequestHeaderArgumentResolver(conversionService));
resolvers.add(new RequestBodyArgumentResolver(this.reactiveAdapterRegistry));
resolvers.add(new PathVariableArgumentResolver(conversionService));
resolvers.add(new RequestParamArgumentResolver(conversionService));
resolvers.add(new CookieValueArgumentResolver(conversionService));
resolvers.add(new RequestAttributeArgumentResolver());
// Specific type
resolvers.add(new UrlArgumentResolver());
resolvers.add(new HttpMethodArgumentResolver());
return resolvers;
} }
@ -183,26 +180,163 @@ public final class HttpServiceProxyFactory implements InitializingBean, Embedded
* @return the created proxy * @return the created proxy
*/ */
public <S> S createClient(Class<S> serviceType) { public <S> S createClient(Class<S> serviceType) {
if (this.builderInitializedFactory != null) {
List<HttpServiceMethod> httpServiceMethods = return this.builderInitializedFactory.createClient(serviceType);
MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream() }
.map(method -> createHttpServiceMethod(serviceType, method)) else if (this.beanStyleFactory != null) {
.toList(); return this.beanStyleFactory.createClient(serviceType);
}
return ProxyFactory.getProxy(serviceType, new HttpServiceMethodInterceptor(httpServiceMethods)); else {
throw new IllegalStateException("Expected Builder initialized or Bean-style delegate");
}
} }
private boolean isExchangeMethod(Method method) {
return AnnotatedElementUtils.hasAnnotation(method, HttpExchange.class); /**
* Return an {@link HttpServiceProxyFactory} builder, initialized with the
* given client.
*/
public static Builder builder(HttpClientAdapter clientAdapter) {
return new Builder().clientAdapter(clientAdapter);
} }
private <S> HttpServiceMethod createHttpServiceMethod(Class<S> serviceType, Method method) { /**
Assert.notNull(this.argumentResolvers, * Return an {@link HttpServiceProxyFactory} builder.
"No argument resolvers: afterPropertiesSet was not called"); */
public static Builder builder() {
return new Builder();
}
/**
* Builder to create an {@link HttpServiceProxyFactory}.
*/
public static final class Builder {
@Nullable
private HttpClientAdapter clientAdapter;
@Nullable
private List<HttpServiceArgumentResolver> argumentResolvers;
@Nullable
private ConversionService conversionService;
@Nullable
private StringValueResolver embeddedValueResolver;
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@Nullable
private Duration blockTimeout;
private Builder() {
}
/**
* Provide the HTTP client to perform requests through.
* @param clientAdapter a client adapted to {@link HttpClientAdapter}
* @return this same builder instance
*/
public Builder clientAdapter(HttpClientAdapter clientAdapter) {
this.clientAdapter = clientAdapter;
return this;
}
/**
* Register a custom argument resolver, invoked ahead of default resolvers.
* @param resolver the resolver to add
* @return this same builder instance
*/
public Builder customArgumentResolver(HttpServiceArgumentResolver resolver) {
this.argumentResolvers = (this.argumentResolvers != null ? this.argumentResolvers : new ArrayList<>());
this.argumentResolvers.add(resolver);
return this;
}
/**
* Set the {@link ConversionService} to use where input values need to
* be formatted as Strings.
* <p>By default this is {@link DefaultFormattingConversionService}.
* @return this same builder instance
*/
public Builder conversionService(ConversionService conversionService) {
this.conversionService = conversionService;
return this;
}
/**
* Set the {@link StringValueResolver} to use for resolving placeholders
* and expressions embedded in {@link HttpExchange#url()}.
* @param embeddedValueResolver the resolver to use
* @return this same builder instance
*/
public Builder embeddedValueResolver(StringValueResolver embeddedValueResolver) {
this.embeddedValueResolver = embeddedValueResolver;
return this;
}
/**
* Set the {@link ReactiveAdapterRegistry} to use to support different
* asynchronous types for HTTP service method return values.
* <p>By default this is {@link ReactiveAdapterRegistry#getSharedInstance()}.
* @return this same builder instance
*/
public Builder reactiveAdapterRegistry(ReactiveAdapterRegistry registry) {
this.reactiveAdapterRegistry = registry;
return this;
}
/**
* Configure how long to wait for a response for an HTTP service method
* with a synchronous (blocking) method signature.
* <p>By default this is 5 seconds.
* @param blockTimeout the timeout value
* @return this same builder instance
*/
public Builder blockTimeout(Duration blockTimeout) {
this.blockTimeout = blockTimeout;
return this;
}
/**
* Build the {@link HttpServiceProxyFactory} instance.
*/
public HttpServiceProxyFactory build() {
Assert.notNull(this.clientAdapter, "HttpClientAdapter is required");
return new HttpServiceProxyFactory(
this.clientAdapter, initArgumentResolvers(),
this.embeddedValueResolver, this.reactiveAdapterRegistry,
(this.blockTimeout != null ? this.blockTimeout : Duration.ofSeconds(5)));
}
private List<HttpServiceArgumentResolver> initArgumentResolvers() {
List<HttpServiceArgumentResolver> resolvers = new ArrayList<>();
// Custom
if (this.argumentResolvers != null) {
resolvers.addAll(this.argumentResolvers);
}
ConversionService service = (this.conversionService != null ?
this.conversionService : new DefaultFormattingConversionService());
// Annotation-based
resolvers.add(new RequestHeaderArgumentResolver(service));
resolvers.add(new RequestBodyArgumentResolver(this.reactiveAdapterRegistry));
resolvers.add(new PathVariableArgumentResolver(service));
resolvers.add(new RequestParamArgumentResolver(service));
resolvers.add(new CookieValueArgumentResolver(service));
resolvers.add(new RequestAttributeArgumentResolver());
// Specific type
resolvers.add(new UrlArgumentResolver());
resolvers.add(new HttpMethodArgumentResolver());
return resolvers;
}
return new HttpServiceMethod(
method, serviceType, this.argumentResolvers, this.clientAdapter,
this.embeddedValueResolver, this.reactiveAdapterRegistry, this.blockTimeout);
} }
@ -235,4 +369,174 @@ public final class HttpServiceProxyFactory implements InitializingBean, Embedded
} }
} }
/**
* Temporary class until bean-style initialization is removed.
*/
private static final class BuilderInitializedFactory {
private final HttpClientAdapter clientAdapter;
private final List<HttpServiceArgumentResolver> argumentResolvers;
@Nullable
private final StringValueResolver embeddedValueResolver;
private final ReactiveAdapterRegistry reactiveAdapterRegistry;
private final Duration blockTimeout;
private BuilderInitializedFactory(
HttpClientAdapter clientAdapter, List<HttpServiceArgumentResolver> argumentResolvers,
@Nullable StringValueResolver embeddedValueResolver,
ReactiveAdapterRegistry reactiveAdapterRegistry, Duration blockTimeout) {
this.clientAdapter = clientAdapter;
this.argumentResolvers = argumentResolvers;
this.embeddedValueResolver = embeddedValueResolver;
this.reactiveAdapterRegistry = reactiveAdapterRegistry;
this.blockTimeout = blockTimeout;
}
public <S> S createClient(Class<S> serviceType) {
List<HttpServiceMethod> httpServiceMethods =
MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream()
.map(method -> createHttpServiceMethod(serviceType, method))
.toList();
return ProxyFactory.getProxy(serviceType, new HttpServiceMethodInterceptor(httpServiceMethods));
}
private boolean isExchangeMethod(Method method) {
return AnnotatedElementUtils.hasAnnotation(method, HttpExchange.class);
}
private <S> HttpServiceMethod createHttpServiceMethod(Class<S> serviceType, Method method) {
Assert.notNull(this.argumentResolvers,
"No argument resolvers: afterPropertiesSet was not called");
return new HttpServiceMethod(
method, serviceType, this.argumentResolvers, this.clientAdapter,
this.embeddedValueResolver, this.reactiveAdapterRegistry, this.blockTimeout);
}
}
/**
* Temporary class to support bean-style initialization during deprecation period.
*/
private static final class BeanStyleFactory implements InitializingBean, EmbeddedValueResolverAware {
private final HttpClientAdapter clientAdapter;
@Nullable
private List<HttpServiceArgumentResolver> customArgumentResolvers;
@Nullable
private List<HttpServiceArgumentResolver> argumentResolvers;
@Nullable
private ConversionService conversionService;
@Nullable
private StringValueResolver embeddedValueResolver;
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
private Duration blockTimeout = Duration.ofSeconds(5);
BeanStyleFactory(HttpClientAdapter clientAdapter) {
Assert.notNull(clientAdapter, "HttpClientAdapter is required");
this.clientAdapter = clientAdapter;
}
public void addCustomArgumentResolver(HttpServiceArgumentResolver resolver) {
if (this.customArgumentResolvers == null) {
this.customArgumentResolvers = new ArrayList<>();
}
this.customArgumentResolvers.add(resolver);
}
public void setCustomArgumentResolvers(List<HttpServiceArgumentResolver> resolvers) {
this.customArgumentResolvers = new ArrayList<>(resolvers);
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) {
this.reactiveAdapterRegistry = registry;
}
public void setBlockTimeout(Duration blockTimeout) {
this.blockTimeout = blockTimeout;
}
@Override
public void afterPropertiesSet() throws Exception {
this.conversionService = (this.conversionService != null ?
this.conversionService : new DefaultFormattingConversionService());
this.argumentResolvers = initArgumentResolvers(this.conversionService);
}
private List<HttpServiceArgumentResolver> initArgumentResolvers(ConversionService conversionService) {
List<HttpServiceArgumentResolver> resolvers = new ArrayList<>();
// Custom
if (this.customArgumentResolvers != null) {
resolvers.addAll(this.customArgumentResolvers);
}
// Annotation-based
resolvers.add(new RequestHeaderArgumentResolver(conversionService));
resolvers.add(new RequestBodyArgumentResolver(this.reactiveAdapterRegistry));
resolvers.add(new PathVariableArgumentResolver(conversionService));
resolvers.add(new RequestParamArgumentResolver(conversionService));
resolvers.add(new CookieValueArgumentResolver(conversionService));
resolvers.add(new RequestAttributeArgumentResolver());
// Specific type
resolvers.add(new UrlArgumentResolver());
resolvers.add(new HttpMethodArgumentResolver());
return resolvers;
}
public <S> S createClient(Class<S> serviceType) {
List<HttpServiceMethod> httpServiceMethods =
MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream()
.map(method -> createHttpServiceMethod(serviceType, method))
.toList();
return ProxyFactory.getProxy(serviceType, new HttpServiceMethodInterceptor(httpServiceMethods));
}
private boolean isExchangeMethod(Method method) {
return AnnotatedElementUtils.hasAnnotation(method, HttpExchange.class);
}
private <S> HttpServiceMethod createHttpServiceMethod(Class<S> serviceType, Method method) {
Assert.notNull(this.argumentResolvers,
"No argument resolvers: afterPropertiesSet was not called");
return new HttpServiceMethod(
method, serviceType, this.argumentResolvers, this.clientAdapter,
this.embeddedValueResolver, this.reactiveAdapterRegistry, this.blockTimeout);
}
}
} }

View File

@ -42,8 +42,7 @@ class CookieValueArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -43,7 +43,7 @@ public class HttpMethodArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet(); proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -64,7 +64,7 @@ public class HttpServiceMethodTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
this.proxyFactory = new HttpServiceProxyFactory(this.client); this.proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
this.proxyFactory.afterPropertiesSet(); this.proxyFactory.afterPropertiesSet();
} }
@ -174,10 +174,10 @@ public class HttpServiceMethodTests {
} }
@Test @Test
void typeAndMethodAnnotatedService() throws Exception { void typeAndMethodAnnotatedService() {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client)
proxyFactory.setEmbeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value)); .embeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value))
proxyFactory.afterPropertiesSet(); .build();
MethodLevelAnnotatedService service = proxyFactory.createClient(TypeAndMethodLevelAnnotatedService.class); MethodLevelAnnotatedService service = proxyFactory.createClient(TypeAndMethodLevelAnnotatedService.class);

View File

@ -61,8 +61,9 @@ class NamedValueArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client)
proxyFactory.addCustomArgumentResolver(this.argumentResolver); .customArgumentResolver(this.argumentResolver)
.build();
proxyFactory.afterPropertiesSet(); proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);

View File

@ -41,7 +41,7 @@ class PathVariableArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet(); proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -40,8 +40,7 @@ class RequestAttributeArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -45,8 +45,7 @@ public class RequestBodyArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -43,8 +43,7 @@ class RequestHeaderArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -45,8 +45,7 @@ public class RequestParamArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -40,8 +40,7 @@ public class UrlArgumentResolverTests {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = new HttpServiceProxyFactory(this.client); HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
proxyFactory.afterPropertiesSet();
this.service = proxyFactory.createClient(Service.class); this.service = proxyFactory.createClient(Service.class);
} }

View File

@ -124,6 +124,15 @@ public final class WebClientAdapter implements HttpClientAdapter {
} }
/**
* Create a {@link WebClientAdapter} for the given {@code WebClient} instance.
* @param webClient the client to use
* @return the created adapter instance
*/
public static WebClientAdapter forClient(WebClient webClient) {
return new WebClientAdapter(webClient);
}
/** /**
* Static method to create a {@link HttpServiceProxyFactory} configured to * Static method to create a {@link HttpServiceProxyFactory} configured to
* use the given {@link WebClient} instance. Effectively a shortcut for: * use the given {@link WebClient} instance. Effectively a shortcut for:
@ -133,7 +142,11 @@ public final class WebClientAdapter implements HttpClientAdapter {
* </pre> * </pre>
* @param webClient the client to use * @param webClient the client to use
* @return the created {@code HttpServiceProxyFactory} instance * @return the created {@code HttpServiceProxyFactory} instance
* @deprecated in favor of using {@link #forClient(WebClient)} and
* {@link HttpServiceProxyFactory#builder(HttpClientAdapter)}
*/ */
@SuppressWarnings("removal")
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public static HttpServiceProxyFactory createHttpServiceProxyFactory(WebClient webClient) { public static HttpServiceProxyFactory createHttpServiceProxyFactory(WebClient webClient) {
return new HttpServiceProxyFactory(new WebClientAdapter(webClient)); return new HttpServiceProxyFactory(new WebClientAdapter(webClient));
} }
@ -143,18 +156,12 @@ public final class WebClientAdapter implements HttpClientAdapter {
* a {@link WebClient.Builder} and uses it to create the client. * a {@link WebClient.Builder} and uses it to create the client.
* @param webClientBuilder a builder to create the client to use with * @param webClientBuilder a builder to create the client to use with
* @return the created {@code HttpServiceProxyFactory} instance * @return the created {@code HttpServiceProxyFactory} instance
* @deprecated in favor of using {@link #forClient(WebClient)} and
* {@link HttpServiceProxyFactory#builder(HttpClientAdapter)}
*/ */
@Deprecated(since = "6.0.0-RC1", forRemoval = true)
public static HttpServiceProxyFactory createHttpServiceProxyFactory(WebClient.Builder webClientBuilder) { public static HttpServiceProxyFactory createHttpServiceProxyFactory(WebClient.Builder webClientBuilder) {
return createHttpServiceProxyFactory(webClientBuilder.build()); return createHttpServiceProxyFactory(webClientBuilder.build());
} }
/**
* Create a {@link WebClientAdapter} for the given {@code WebClient} instance.
* @param webClient the client to use
* @return the created adapter instance
*/
public static WebClientAdapter forClient(WebClient webClient) {
return new WebClientAdapter(webClient);
}
} }

View File

@ -105,10 +105,11 @@ public class WebClientHttpServiceProxyTests {
return initHttpService(webClient); return initHttpService(webClient);
} }
private TestHttpService initHttpService(WebClient webClient) throws Exception { private TestHttpService initHttpService(WebClient webClient) {
HttpServiceProxyFactory factory = WebClientAdapter.createHttpServiceProxyFactory(webClient); return HttpServiceProxyFactory.builder()
factory.afterPropertiesSet(); .clientAdapter(WebClientAdapter.forClient(webClient))
return factory.createClient(TestHttpService.class); .build()
.createClient(TestHttpService.class);
} }
private void prepareResponse(Consumer<MockResponse> consumer) { private void prepareResponse(Consumer<MockResponse> consumer) {

View File

@ -390,8 +390,7 @@ Two, create a proxy that will perform the declared HTTP exchanges:
[source,java,indent=0,subs="verbatim,quotes"] [source,java,indent=0,subs="verbatim,quotes"]
---- ----
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build(); WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = WebClientAdapter.createHttpServiceProxyFactory(client); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
factory.afterPropertiesSet();
RepositoryService service = factory.createClient(RepositoryService.class); RepositoryService service = factory.createClient(RepositoryService.class);
---- ----