mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Option to supply client builder in HttpServiceGroupConfigurer
Closes gh-35707
This commit is contained in:
+27
-6
@@ -64,20 +64,30 @@ public interface HttpServiceGroupConfigurer<CB> extends Ordered {
|
||||
Groups<CB> filter(Predicate<HttpServiceGroup> predicate);
|
||||
|
||||
/**
|
||||
* Configure the client builder for each
|
||||
* {@link #filter(Predicate) filtered} group.
|
||||
* Callback to customize the client builder for every group matched by
|
||||
* the specified name or predicate filters, or to all groups
|
||||
* if no filters are specified.
|
||||
*/
|
||||
void forEachClient(ClientCallback<CB> callback);
|
||||
|
||||
/**
|
||||
* Configure the {@code HttpServiceProxyFactory} for each
|
||||
* {@link #filter(Predicate) filtered} group.
|
||||
* Callback to supply the client builder for every group matched by
|
||||
* the specified name or predicate filters, or to all groups
|
||||
* if no filters are specified.
|
||||
*/
|
||||
void forEachClient(InitializingClientCallback<CB> callback);
|
||||
|
||||
/**
|
||||
* Callback to customize the proxy factory for every group matched by
|
||||
* the specified name or predicate filters, or to all groups
|
||||
* if no filters are specified.
|
||||
*/
|
||||
void forEachProxyFactory(ProxyFactoryCallback callback);
|
||||
|
||||
/**
|
||||
* Configure the client builder and {@code HttpServiceProxyFactory} for each
|
||||
* {@link #filter(Predicate) filtered} group.
|
||||
* Callback to customize the client builder and the proxy factory for
|
||||
* every group matched by the specified name or predicate filters,
|
||||
* or to all groups if no filters are specified.
|
||||
*/
|
||||
void forEachGroup(GroupCallback<CB> callback);
|
||||
}
|
||||
@@ -94,6 +104,17 @@ public interface HttpServiceGroupConfigurer<CB> extends Ordered {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to provide the client builder rather than customize it.
|
||||
* @param <CB> the type of client builder, i.e. {@code RestClient} or {@code WebClient} builder.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface InitializingClientCallback<CB> {
|
||||
|
||||
CB initClient(HttpServiceGroup group);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to configure the {@code HttpServiceProxyFactory} for a given group.
|
||||
*/
|
||||
|
||||
+27
-16
@@ -131,16 +131,16 @@ public final class HttpServiceProxyRegistryFactoryBean
|
||||
|
||||
private static class GroupAdapterInitializer {
|
||||
|
||||
private static final String REST_CLIENT_HTTP_SERVICE_GROUP_ADAPTER = "org.springframework.web.client.support.RestClientHttpServiceGroupAdapter";
|
||||
private static final String REST_CLIENT_HTTP_SERVICE_GROUP_ADAPTER =
|
||||
"org.springframework.web.client.support.RestClientHttpServiceGroupAdapter";
|
||||
|
||||
private static final String WEB_CLIENT_HTTP_SERVICE_GROUP_ADAPTER = "org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupAdapter";
|
||||
private static final String WEB_CLIENT_HTTP_SERVICE_GROUP_ADAPTER =
|
||||
"org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupAdapter";
|
||||
|
||||
static Map<HttpServiceGroup.ClientType, HttpServiceGroupAdapter<?>> initGroupAdapters() {
|
||||
Map<HttpServiceGroup.ClientType, HttpServiceGroupAdapter<?>> map = new LinkedHashMap<>(2);
|
||||
|
||||
addGroupAdapter(map, HttpServiceGroup.ClientType.REST_CLIENT, REST_CLIENT_HTTP_SERVICE_GROUP_ADAPTER);
|
||||
addGroupAdapter(map, HttpServiceGroup.ClientType.WEB_CLIENT, WEB_CLIENT_HTTP_SERVICE_GROUP_ADAPTER);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -167,14 +167,13 @@ public final class HttpServiceProxyRegistryFactoryBean
|
||||
|
||||
private final HttpServiceGroupAdapter<?> groupAdapter;
|
||||
|
||||
private final Object clientBuilder;
|
||||
private @Nullable Object clientBuilder;
|
||||
|
||||
private final HttpServiceProxyFactory.Builder proxyFactoryBuilder = HttpServiceProxyFactory.builder();
|
||||
|
||||
ConfigurableGroup(HttpServiceGroup group) {
|
||||
this.group = group;
|
||||
this.groupAdapter = getGroupAdapter(group.clientType());
|
||||
this.clientBuilder = this.groupAdapter.createClientBuilder();
|
||||
}
|
||||
|
||||
private static HttpServiceGroupAdapter<?> getGroupAdapter(HttpServiceGroup.ClientType clientType) {
|
||||
@@ -191,32 +190,39 @@ public final class HttpServiceProxyRegistryFactoryBean
|
||||
return this.group;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <CB> void applyClientCallback(HttpServiceGroupConfigurer.ClientCallback<CB> callback) {
|
||||
callback.withClient(this.group, (CB) this.clientBuilder);
|
||||
callback.withClient(this.group, getClientBuilder());
|
||||
}
|
||||
|
||||
public <CB> void applyClientCallback(HttpServiceGroupConfigurer.InitializingClientCallback<CB> callback) {
|
||||
Assert.state(this.clientBuilder == null, "Client builder already initialized");
|
||||
this.clientBuilder = callback.initClient(this.group);
|
||||
}
|
||||
|
||||
public void applyProxyFactoryCallback(HttpServiceGroupConfigurer.ProxyFactoryCallback callback) {
|
||||
callback.withProxyFactory(this.group, this.proxyFactoryBuilder);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <CB> void applyGroupCallback(HttpServiceGroupConfigurer.GroupCallback<CB> callback) {
|
||||
callback.withGroup(this.group, (CB) this.clientBuilder, this.proxyFactoryBuilder);
|
||||
callback.withGroup(this.group, getClientBuilder(), this.proxyFactoryBuilder);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <CB> CB getClientBuilder() {
|
||||
if (this.clientBuilder == null) {
|
||||
this.clientBuilder = this.groupAdapter.createClientBuilder();
|
||||
}
|
||||
return (CB) this.clientBuilder;
|
||||
}
|
||||
|
||||
public Map<Class<?>, Object> createProxies() {
|
||||
Map<Class<?>, Object> map = new LinkedHashMap<>(this.group.httpServiceTypes().size());
|
||||
HttpServiceProxyFactory factory = this.proxyFactoryBuilder.exchangeAdapter(initExchangeAdapter()).build();
|
||||
HttpExchangeAdapter adapter = this.groupAdapter.createExchangeAdapter(getClientBuilder());
|
||||
HttpServiceProxyFactory factory = this.proxyFactoryBuilder.exchangeAdapter(adapter).build();
|
||||
this.group.httpServiceTypes().forEach(type -> map.put(type, factory.createClient(type)));
|
||||
return map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <CB> HttpExchangeAdapter initExchangeAdapter() {
|
||||
return ((HttpServiceGroupAdapter<CB>) this.groupAdapter).createExchangeAdapter((CB) this.clientBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[name=" + name() + "]";
|
||||
@@ -258,6 +264,11 @@ public final class HttpServiceProxyRegistryFactoryBean
|
||||
filterAndReset().forEach(group -> group.applyClientCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachClient(HttpServiceGroupConfigurer.InitializingClientCallback<CB> callback) {
|
||||
filterAndReset().forEach(group -> group.applyClientCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachProxyFactory(HttpServiceGroupConfigurer.ProxyFactoryCallback callback) {
|
||||
filterAndReset().forEach(group -> group.applyProxyFactoryCallback(callback));
|
||||
|
||||
+50
-20
@@ -20,8 +20,10 @@ import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClient;
|
||||
@@ -31,8 +33,14 @@ import org.springframework.web.service.registry.echo.EchoA;
|
||||
import org.springframework.web.service.registry.echo.EchoB;
|
||||
import org.springframework.web.service.registry.greeting.GreetingA;
|
||||
import org.springframework.web.service.registry.greeting.GreetingB;
|
||||
import org.springframework.web.testfixture.http.client.MockClientHttpRequest;
|
||||
import org.springframework.web.testfixture.http.client.MockClientHttpResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.web.service.registry.HttpServiceGroup.ClientType.REST_CLIENT;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HttpServiceProxyRegistryFactoryBean}.
|
||||
@@ -42,43 +50,65 @@ public class HttpServiceProxyRegistryFactoryBeanTests {
|
||||
|
||||
@Test
|
||||
void twoGroups() {
|
||||
|
||||
GroupsMetadata groupsMetadata = new GroupsMetadata();
|
||||
|
||||
String echoName = "echo";
|
||||
String greetingName = "greeting";
|
||||
GroupsMetadata groupsMetadata = new GroupsMetadata();
|
||||
|
||||
groupsMetadata.getOrCreateGroup(echoName, HttpServiceGroup.ClientType.REST_CLIENT)
|
||||
.httpServiceTypeNames().addAll(List.of(EchoA.class.getName(), EchoB.class.getName()));
|
||||
List<String> echoServices = List.of(EchoA.class.getName(), EchoB.class.getName());
|
||||
groupsMetadata.getOrCreateGroup(echoName, REST_CLIENT).httpServiceTypeNames().addAll(echoServices);
|
||||
|
||||
groupsMetadata.getOrCreateGroup(greetingName, HttpServiceGroup.ClientType.REST_CLIENT)
|
||||
.httpServiceTypeNames().addAll(List.of(GreetingA.class.getName(), GreetingB.class.getName()));
|
||||
List<String> greetingServices = List.of(GreetingA.class.getName(), GreetingB.class.getName());
|
||||
groupsMetadata.getOrCreateGroup(greetingName, REST_CLIENT).httpServiceTypeNames().addAll(greetingServices);
|
||||
|
||||
Predicate<HttpServiceGroup> echoFilter = group -> group.name().equals(echoName);
|
||||
Predicate<HttpServiceGroup> greetingFilter = group -> group.name().equals(greetingName);
|
||||
TestConfigurer groupConfigurer = new TestConfigurer(List.of(echoFilter, greetingFilter));
|
||||
|
||||
TestConfigurer testConfigurer = new TestConfigurer(List.of(echoFilter, greetingFilter));
|
||||
|
||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(TestConfigurer.class, () -> testConfigurer);
|
||||
applicationContext.refresh();
|
||||
|
||||
HttpServiceProxyRegistryFactoryBean factoryBean = new HttpServiceProxyRegistryFactoryBean(groupsMetadata);
|
||||
factoryBean.setApplicationContext(applicationContext);
|
||||
factoryBean.setBeanClassLoader(getClass().getClassLoader());
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
HttpServiceProxyRegistry registry = factoryBean.getObject();
|
||||
HttpServiceProxyRegistry registry = initProxyRegistry(groupConfigurer, groupsMetadata);
|
||||
assertThat(registry.getGroupNames()).containsExactlyInAnyOrder(echoName, greetingName);
|
||||
assertThat(registry.getClientTypesInGroup(echoName)).containsExactlyInAnyOrder(EchoA.class, EchoB.class);
|
||||
assertThat(registry.getClientTypesInGroup(greetingName)).containsExactlyInAnyOrder(GreetingA.class, GreetingB.class);
|
||||
|
||||
assertThat(testConfigurer.invocations)
|
||||
assertThat(groupConfigurer.invocations)
|
||||
.containsKeys(echoFilter, greetingFilter)
|
||||
.containsEntry(echoFilter, List.of(echoName))
|
||||
.containsEntry(greetingFilter, List.of(greetingName));
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializeClientBuilder() throws Exception {
|
||||
GroupsMetadata groupsMetadata = new GroupsMetadata();
|
||||
groupsMetadata.getOrCreateGroup("echo", REST_CLIENT).httpServiceTypeNames().add(EchoA.class.getName());
|
||||
|
||||
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
|
||||
MockClientHttpRequest request = new MockClientHttpRequest();
|
||||
request.setResponse(new MockClientHttpResponse());
|
||||
when(requestFactory.createRequest(any(), any())).thenReturn(request);
|
||||
|
||||
RestClient.Builder clientBuilder = RestClient.builder().baseUrl("/").requestFactory(requestFactory);
|
||||
RestClientHttpServiceGroupConfigurer groupConfigurer = groups -> groups.forEachClient(group -> clientBuilder);
|
||||
|
||||
HttpServiceProxyRegistry registry = initProxyRegistry(groupConfigurer, groupsMetadata);
|
||||
registry.getClient(EchoA.class).handle("foo");
|
||||
|
||||
verify(requestFactory);
|
||||
}
|
||||
|
||||
private HttpServiceProxyRegistry initProxyRegistry(
|
||||
RestClientHttpServiceGroupConfigurer groupConfigurer, GroupsMetadata groupsMetadata) {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.registerBean(RestClientHttpServiceGroupConfigurer.class, () -> groupConfigurer);
|
||||
context.refresh();
|
||||
|
||||
HttpServiceProxyRegistryFactoryBean factoryBean = new HttpServiceProxyRegistryFactoryBean(groupsMetadata);
|
||||
factoryBean.setApplicationContext(context);
|
||||
factoryBean.setBeanClassLoader(getClass().getClassLoader());
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
return factoryBean.getObject();
|
||||
}
|
||||
|
||||
|
||||
private static class TestConfigurer implements RestClientHttpServiceGroupConfigurer {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user