Use bean class loader when creating interface clients
Update interface client code to replace `GroupsMetadata.loadClass` calls with `ClassUtils.resolveClassName` passing in the bean class loader. Since the bean class loader in injected after construction, some minor refactoring has been applied to `HttpServiceProxyRegistryFactoryBean`. The class now stores `GroupsMetadata` and only loads the types in in `afterPropertiesSet`. The `HttpServiceProxyFactory` class has also been updated to ensure that the proxy is created using the class loader of the service type, rather than the thread context class loader. Fixes gh-34846
This commit is contained in:
committed by
rstoyanchev
parent
13f9fed4bc
commit
abbee1a050
@@ -23,6 +23,7 @@ import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
@@ -30,7 +31,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.service.registry.AbstractHttpServiceRegistrar;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
@@ -112,6 +115,22 @@ public class RestClientProxyRegistryIntegrationTests {
|
||||
assertThat(request.getPath()).isEqualTo("/greetingB?input=b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void beansAreCreatedUsingBeanClassLoader() {
|
||||
ClassLoader beanClassLoader = new OverridingClassLoader(getClass().getClassLoader()) {
|
||||
|
||||
protected boolean isEligibleForOverriding(String className) {
|
||||
return className.contains("EchoA");
|
||||
};
|
||||
};
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.setClassLoader(beanClassLoader);
|
||||
context.register(ClassUtils.resolveClassName(ListingConfig.class.getName(), beanClassLoader));
|
||||
context.refresh();
|
||||
assertThat(context.getBean(ClassUtils.resolveClassName(EchoA.class.getName(), beanClassLoader))
|
||||
.getClass()
|
||||
.getClassLoader()).isSameAs(beanClassLoader);
|
||||
}
|
||||
|
||||
private static class ClientConfig {
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class GroupsMetadataValueDelegateTests {
|
||||
@Test
|
||||
void generateGroupsMetadataEmpty() {
|
||||
compile(new GroupsMetadata(), (instance, compiled) -> assertThat(instance)
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups()).isEmpty()));
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups(compiled.getClassLoader())).isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,7 +100,7 @@ class GroupsMetadataValueDelegateTests {
|
||||
GroupsMetadata groupsMetadata = new GroupsMetadata();
|
||||
groupsMetadata.getOrCreateGroup("test-group", ClientType.REST_CLIENT).httpServiceTypeNames().add(EchoA.class.getName());
|
||||
compile(groupsMetadata, (instance, compiled) -> assertThat(instance)
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups())
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups(compiled.getClassLoader()))
|
||||
.singleElement().satisfies(hasHttpServiceGroup("test-group", ClientType.REST_CLIENT, EchoA.class))));
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class GroupsMetadataValueDelegateTests {
|
||||
Function<GeneratedClass, ValueCodeGenerator> valueCodeGeneratorFactory = generatedClass ->
|
||||
ValueCodeGenerator.withDefaults().add(List.of(new GroupsMetadataValueDelegate()));
|
||||
compile(valueCodeGeneratorFactory, groupsMetadata, (instance, compiled) -> assertThat(instance)
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups())
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups(compiled.getClassLoader()))
|
||||
.satisfiesOnlyOnce(hasHttpServiceGroup("test-group", ClientType.REST_CLIENT, EchoA.class, EchoB.class))
|
||||
.satisfiesOnlyOnce(hasHttpServiceGroup("another-group", ClientType.WEB_CLIENT, GreetingA.class, GreetingB.class))
|
||||
.hasSize(2)));
|
||||
@@ -130,7 +130,7 @@ class GroupsMetadataValueDelegateTests {
|
||||
.addAll(List.of(GreetingA.class.getName(), GreetingB.class.getName()));
|
||||
|
||||
compile(groupsMetadata, (instance, compiled) -> assertThat(instance)
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups())
|
||||
.isInstanceOfSatisfying(GroupsMetadata.class, metadata -> assertThat(metadata.groups(compiled.getClassLoader()))
|
||||
.satisfiesOnlyOnce(hasHttpServiceGroup("test-group", ClientType.REST_CLIENT, EchoA.class, EchoB.class))
|
||||
.satisfiesOnlyOnce(hasHttpServiceGroup("another-group", ClientType.WEB_CLIENT, GreetingA.class, GreetingB.class))
|
||||
.hasSize(2)));
|
||||
|
||||
@@ -159,7 +159,7 @@ public class HttpServiceRegistrarTests {
|
||||
GroupsMetadata metadata = (GroupsMetadata) valueHolder.getValue();
|
||||
assertThat(metadata).isNotNull();
|
||||
|
||||
return metadata.groups().stream()
|
||||
return metadata.groups(null).stream()
|
||||
.collect(Collectors.toMap(HttpServiceGroup::name, Function.identity()));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user