Add FeignChildContextInitializer.

This commit is contained in:
Olga Maciaszek-Sharma
2022-11-24 19:08:48 +01:00
parent 5fb3790dab
commit f9cc51c16b
28 changed files with 372 additions and 72 deletions

View File

@@ -25,7 +25,7 @@ import feign.Target;
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}

View File

@@ -55,11 +55,13 @@ import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.openfeign.aot.FeignChildContextInitializer;
import org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor;
import org.springframework.cloud.openfeign.support.FeignEncoderProperties;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.SortJacksonModule;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@@ -102,12 +104,18 @@ public class FeignAutoConfiguration {
}
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
public FeignClientFactory feignContext() {
FeignClientFactory context = new FeignClientFactory();
context.setConfigurations(this.configurations);
return context;
}
@Bean
static FeignChildContextInitializer feignChildContextInitializer(ApplicationContext parentContext,
FeignClientFactory feignClientFactory) {
return new FeignChildContextInitializer(parentContext, feignClientFactory);
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.openfeign.cache.enabled", matchIfMissing = true)
@ConditionalOnBean(CacheInterceptor.class)
@@ -116,7 +124,7 @@ public class FeignAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Module.class, Page.class, Sort.class })
@ConditionalOnClass({Module.class, Page.class, Sort.class})
@ConditionalOnProperty(value = "spring.cloud.openfeign.autoconfiguration.jackson.enabled", havingValue = "true",
matchIfMissing = true)
protected static class FeignJacksonConfiguration {

View File

@@ -39,7 +39,7 @@ class FeignCircuitBreakerTargeter implements Targeter {
}
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof FeignCircuitBreaker.Builder builder)) {
return feign.target(target);
@@ -56,20 +56,20 @@ class FeignCircuitBreakerTargeter implements Targeter {
return builder(name, builder).target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
private <T> T targetWithFallbackFactory(String feignClientName, FeignClientFactory context,
Target.HardCodedTarget<T> target, FeignCircuitBreaker.Builder builder, Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext("fallbackFactory",
feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
return builder(feignClientName, builder).target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget<T> target,
FeignCircuitBreaker.Builder builder, Class<?> fallback) {
private <T> T targetWithFallback(String feignClientName, FeignClientFactory context,
Target.HardCodedTarget<T> target, FeignCircuitBreaker.Builder builder, Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
return builder(feignClientName, builder).target(target, fallbackInstance);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignClientFactory context,
Class<?> beanType, Class<T> targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {

View File

@@ -31,10 +31,11 @@ import org.springframework.lang.Nullable;
* @author Dave Syer
* @author Matt King
* @author Jasbir Singh
* @author Olga Maciaszek-Sharma
*/
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public class FeignClientFactory extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
public FeignClientFactory() {
super(FeignClientsConfiguration.class, "spring.cloud.openfeign", "spring.cloud.openfeign.client.name");
}

View File

@@ -119,7 +119,7 @@ public class FeignClientFactoryBean
Assert.hasText(name, "Name must be set");
}
protected Feign.Builder feign(FeignContext context) {
protected Feign.Builder feign(FeignClientFactory context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(type);
@@ -137,7 +137,7 @@ public class FeignClientFactoryBean
return builder;
}
private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) {
private void applyBuildCustomizers(FeignClientFactory context, Feign.Builder builder) {
Map<String, FeignBuilderCustomizer> customizerMap = context.getInstances(contextId,
FeignBuilderCustomizer.class);
@@ -148,7 +148,7 @@ public class FeignClientFactoryBean
additionalCustomizers.forEach(customizer -> customizer.customize(builder));
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
protected void configureFeign(FeignClientFactory context, Feign.Builder builder) {
FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
: applicationContext.getBean(FeignClientProperties.class);
@@ -172,7 +172,7 @@ public class FeignClientFactoryBean
}
}
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
protected void configureUsingConfiguration(FeignClientFactory context, Feign.Builder builder) {
Logger.Level level = getInheritedAwareOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
@@ -340,7 +340,7 @@ public class FeignClientFactoryBean
}
}
protected <T> T get(FeignContext context, Class<T> type) {
protected <T> T get(FeignClientFactory context, Class<T> type) {
T instance = context.getInstance(contextId, type);
if (instance == null) {
throw new IllegalStateException("No bean found of type " + type + " for " + contextId);
@@ -348,11 +348,11 @@ public class FeignClientFactoryBean
return instance;
}
protected <T> T getOptional(FeignContext context, Class<T> type) {
protected <T> T getOptional(FeignClientFactory context, Class<T> type) {
return context.getInstance(contextId, type);
}
protected <T> T getInheritedAwareOptional(FeignContext context, Class<T> type) {
protected <T> T getInheritedAwareOptional(FeignClientFactory context, Class<T> type) {
if (inheritParentContext) {
return getOptional(context, type);
}
@@ -361,7 +361,7 @@ public class FeignClientFactoryBean
}
}
protected <T> Map<String, T> getInheritedAwareInstances(FeignContext context, Class<T> type) {
protected <T> Map<String, T> getInheritedAwareInstances(FeignClientFactory context, Class<T> type) {
if (inheritParentContext) {
return context.getInstances(contextId, type);
}
@@ -370,7 +370,7 @@ public class FeignClientFactoryBean
}
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
protected <T> T loadBalance(Feign.Builder builder, FeignClientFactory context, HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
@@ -389,7 +389,7 @@ public class FeignClientFactoryBean
* @param contextId name of feign client
* @return returns Options found in context
*/
protected Request.Options getOptionsByName(FeignContext context, String contextId) {
protected Request.Options getOptionsByName(FeignClientFactory context, String contextId) {
if (refreshableClient) {
return context.getInstance(contextId, Request.Options.class.getCanonicalName() + "-" + contextId,
Request.Options.class);
@@ -409,8 +409,8 @@ public class FeignClientFactoryBean
*/
@SuppressWarnings("unchecked")
<T> T getTarget() {
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
FeignClientFactory context = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
: applicationContext.getBean(FeignClientFactory.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {
@@ -468,7 +468,7 @@ public class FeignClientFactoryBean
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> HardCodedTarget<T> resolveTarget(FeignContext context, String contextId, String url) {
private <T> HardCodedTarget<T> resolveTarget(FeignClientFactory context, String contextId, String url) {
if (StringUtils.hasText(url)) {
return new HardCodedTarget(type, name, url);
}

View File

@@ -24,7 +24,7 @@ import feign.Target;
*/
public interface Targeter {
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
Target.HardCodedTarget<T> target);
}

View File

@@ -0,0 +1,155 @@
package org.springframework.cloud.openfeign.aot;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.lang.model.element.Modifier;
import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.ClassName;
import org.springframework.util.Assert;
/**
* A {@link BeanRegistrationAotProcessor} that creates an {@link BeanRegistrationAotContribution} for
* Feign child contexts.
*
* @author Olga Maciaszek-Sharma
* @since 4.0.0
*/
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener<WebServerInitializedEvent> {
private final ApplicationContext applicationContext;
private final FeignClientFactory feignClientFactory;
private final Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers;
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory) {
this(applicationContext, feignClientFactory, new HashMap<>());
}
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) {
this.applicationContext = applicationContext;
this.feignClientFactory = feignClientFactory;
this.applicationContextInitializers = applicationContextInitializers;
}
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext);
BeanFactory applicationBeanFactory = context.getBeanFactory();
if (!(registeredBean.getBeanClass().equals(getClass())
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) {
return null;
}
Set<String> contextIds = new HashSet<>(getContextIdsFromConfig());
Map<String, GenericApplicationContext> childContextAotContributions = contextIds.stream()
.map(contextId -> Map.entry(contextId, buildChildContext(contextId)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return new AotContribution(childContextAotContributions);
}
private GenericApplicationContext buildChildContext(String contextId) {
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId);
feignClientFactory.registerBeans(contextId, childContext);
return childContext;
}
private Collection<String> getContextIdsFromConfig() {
Map<String, FeignClientSpecification> configurations = feignClientFactory.getConfigurations();
return configurations.keySet().stream().filter(key -> !key.startsWith("default."))
.collect(Collectors.toSet());
}
@SuppressWarnings("unchecked")
public FeignChildContextInitializer withApplicationContextInitializers(
Map<String, Object> applicationContextInitializers) {
Map<String, ApplicationContextInitializer<GenericApplicationContext>> convertedInitializers = new HashMap<>();
applicationContextInitializers.keySet()
.forEach(contextId -> convertedInitializers.put(contextId,
(ApplicationContextInitializer<GenericApplicationContext>) applicationContextInitializers
.get(contextId)));
return new FeignChildContextInitializer(applicationContext, feignClientFactory,
convertedInitializers);
}
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
if (applicationContext.equals(event.getApplicationContext())) {
applicationContextInitializers.keySet().forEach(contextId -> {
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId);
applicationContextInitializers.get(contextId).initialize(childContext);
feignClientFactory.addContext(contextId, childContext);
childContext.refresh();
});
}
}
@Override
public boolean isBeanExcludedFromAotProcessing() {
return false;
}
private static class AotContribution implements BeanRegistrationAotContribution {
private final Map<String, GenericApplicationContext> childContexts;
public AotContribution(Map<String, GenericApplicationContext> childContexts) {
this.childContexts = childContexts.entrySet().stream()
.filter(entry -> entry.getValue() != null)
.map(entry -> Map.entry(entry.getKey(), entry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
@Override
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
Map<String, ClassName> generatedInitializerClassNames = childContexts.entrySet()
.stream()
.map(entry -> {
String name = entry.getValue().getDisplayName();
name = name.replaceAll("[-]", "_");
GenerationContext childGenerationContext = generationContext.withName(name);
ClassName initializerClassName = new ApplicationContextAotGenerator()
.processAheadOfTime(entry.getValue(), childGenerationContext);
return Map.entry(entry.getKey(), initializerClassName);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
GeneratedMethod postProcessorMethod = beanRegistrationCode.getMethods()
.add("addFeignChildContextInitializer",
method -> {
method.addJavadoc("Use AOT child context management initialization")
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.addParameter(RegisteredBean.class, "registeredBean")
.addParameter(FeignChildContextInitializer.class, "instance")
.returns(FeignChildContextInitializer.class)
.addStatement("$T<String, Object> initializers = new $T<>()", Map.class, HashMap.class);
generatedInitializerClassNames.keySet()
.forEach(contextId -> method.addStatement("initializers.put($S, new $L())", contextId,
generatedInitializerClassNames.get(contextId)));
method.addStatement("return instance.withApplicationContextInitializers(initializers)");
});
beanRegistrationCode.addInstancePostProcessor(postProcessorMethod.toMethodReference());
}
}
}

View File

@@ -34,11 +34,11 @@ import org.springframework.test.annotation.DirtiesContext;
class EnableFeignClientsSpringDataTests {
@Autowired
private FeignContext feignContext;
private FeignClientFactory feignClientFactory;
@Test
void encoderDefaultCorrect() {
PageableSpringEncoder.class.cast(this.feignContext.getInstance("foo", Encoder.class));
PageableSpringEncoder.class.cast(this.feignClientFactory.getInstance("foo", Encoder.class));
}
@Configuration(proxyBeanMethods = false)

View File

@@ -162,8 +162,8 @@ class FeignBuilderCustomizerTests {
protected static class SampleConfiguration2 {
@Bean
FeignContext feignContext() {
return new FeignContext();
FeignClientFactory feignContext() {
return new FeignClientFactory();
}
@Bean
@@ -198,8 +198,8 @@ class FeignBuilderCustomizerTests {
protected static class SampleConfiguration3 {
@Bean
FeignContext feignContext() {
return new FeignContext();
FeignClientFactory feignContext() {
return new FeignClientFactory();
}
@Bean

View File

@@ -158,7 +158,8 @@ class FeignClientBuilderTests {
@Test
void forType_build() {
// given:
Mockito.when(this.applicationContext.getBean(FeignContext.class)).thenThrow(new ClosedFileSystemException()); // throw
Mockito.when(this.applicationContext.getBean(FeignClientFactory.class))
.thenThrow(new ClosedFileSystemException()); // throw
// an
// unusual
// exception

View File

@@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class FeignClientDisabledClientLevelFeaturesTests {
@Autowired
private FeignContext context;
private FeignClientFactory context;
@Autowired
private FooClient foo;

View File

@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class FeignClientDisabledFeaturesTests {
@Autowired
private FeignContext context;
private FeignClientFactory context;
@Autowired
private FooClient foo;

View File

@@ -50,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class FeignClientErrorDecoderTests {
@Autowired
private FeignContext context;
private FeignClientFactory context;
@Autowired
private FooClient foo;

View File

@@ -30,18 +30,18 @@ import org.springframework.context.annotation.Import;
import static org.assertj.core.api.Assertions.assertThat;
class FeignContextTest {
class FeignClientFactoryTest {
@Test
void getInstanceWithoutAncestors_verifyNullForMissing() {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.refresh();
FeignContext feignContext = new FeignContext();
feignContext.setApplicationContext(parent);
feignContext.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
FeignClientFactory feignClientFactory = new FeignClientFactory();
feignClientFactory.setApplicationContext(parent);
feignClientFactory.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
Logger.Level level = feignContext.getInstanceWithoutAncestors("empty", Logger.Level.class);
Logger.Level level = feignClientFactory.getInstanceWithoutAncestors("empty", Logger.Level.class);
assertThat(level).as("Logger was not null").isNull();
}
@@ -55,11 +55,11 @@ class FeignContextTest {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.refresh();
FeignContext feignContext = new FeignContext();
feignContext.setApplicationContext(parent);
feignContext.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
FeignClientFactory feignClientFactory = new FeignClientFactory();
feignClientFactory.setApplicationContext(parent);
feignClientFactory.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
Collection<RequestInterceptor> interceptors = feignContext
Collection<RequestInterceptor> interceptors = feignClientFactory
.getInstancesWithoutAncestors("empty", RequestInterceptor.class).values();
assertThat(interceptors).as("Interceptors is not empty").isEmpty();
@@ -70,11 +70,11 @@ class FeignContextTest {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.refresh();
FeignContext feignContext = new FeignContext();
feignContext.setApplicationContext(parent);
feignContext.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
FeignClientFactory feignClientFactory = new FeignClientFactory();
feignClientFactory.setApplicationContext(parent);
feignClientFactory.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
Logger.Level level = feignContext.getInstanceWithoutAncestors("demo", Logger.Level.class);
Logger.Level level = feignClientFactory.getInstanceWithoutAncestors("demo", Logger.Level.class);
assertThat(level).isEqualTo(Logger.Level.FULL);
}
@@ -84,11 +84,11 @@ class FeignContextTest {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.refresh();
FeignContext feignContext = new FeignContext();
feignContext.setApplicationContext(parent);
feignContext.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
FeignClientFactory feignClientFactory = new FeignClientFactory();
feignClientFactory.setApplicationContext(parent);
feignClientFactory.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
Collection<RequestInterceptor> interceptors = feignContext
Collection<RequestInterceptor> interceptors = feignClientFactory
.getInstancesWithoutAncestors("demo", RequestInterceptor.class).values();
assertThat(interceptors.size()).isEqualTo(1);

View File

@@ -52,7 +52,7 @@ public class FeignClientFactoryTests {
public void testChildContexts() {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.refresh();
FeignContext context = new FeignContext();
FeignClientFactory context = new FeignClientFactory();
context.setApplicationContext(parent);
context.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class), getSpec("bar", BarConfig.class)));
@@ -103,11 +103,11 @@ public class FeignClientFactoryTests {
}
@Bean
FeignContext feignContext() {
FeignContext feignContext = new FeignContext();
feignContext.setConfigurations(Collections.singletonList(
FeignClientFactory feignContext() {
FeignClientFactory feignClientFactory = new FeignClientFactory();
feignClientFactory.setConfigurations(Collections.singletonList(
new FeignClientSpecification("test", new Class[] { LoadBalancerAutoConfiguration.class })));
return feignContext;
return feignClientFactory;
}
@Bean

View File

@@ -63,7 +63,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
class FeignClientOverrideDefaultsTests {
@Autowired
private FeignContext context;
private FeignClientFactory context;
@Autowired
private FooClient foo;

View File

@@ -55,7 +55,7 @@ class FeignClientUsingConfigurerTest {
private ConfigurableListableBeanFactory beanFactory;
@Autowired
private FeignContext context;
private FeignClientFactory context;
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test

View File

@@ -87,7 +87,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
public class FeignClientUsingPropertiesTests {
@Autowired
FeignContext context;
FeignClientFactory context;
@Autowired
private ApplicationContext applicationContext;

View File

@@ -47,8 +47,8 @@ class FeignCompressionTests {
FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class))
.run(context -> {
FeignContext feignContext = context.getBean(FeignContext.class);
Map<String, RequestInterceptor> interceptors = feignContext.getInstances("foo",
FeignClientFactory feignClientFactory = context.getBean(FeignClientFactory.class);
Map<String, RequestInterceptor> interceptors = feignClientFactory.getInstances("foo",
RequestInterceptor.class);
assertThat(interceptors.size()).isEqualTo(2);
assertThat(interceptors.get("feignAcceptGzipEncodingInterceptor"))

View File

@@ -153,7 +153,7 @@ class FeignHttpClientUrlTests {
public Targeter feignTargeter() {
return new Targeter() {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
Target.HardCodedTarget<T> target) {
Field field = ReflectionUtils.findField(Feign.Builder.class, "client");
ReflectionUtils.makeAccessible(field);

View File

@@ -154,7 +154,7 @@ class FeignHttpClientUrlWithRetryableLoadBalancerTests {
public Targeter feignTargeter() {
return new Targeter() {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
Target.HardCodedTarget<T> target) {
Field field = ReflectionUtils.findField(Feign.Builder.class, "client");
ReflectionUtils.makeAccessible(field);

View File

@@ -50,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class GzipDecodingTests extends FeignClientFactoryBean {
@Autowired
FeignContext context;
FeignClientFactory context;
@Value("${local.server.port}")
private int port = 0;

View File

@@ -53,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class SpringDecoderTests extends FeignClientFactoryBean {
@Autowired
FeignContext context;
FeignClientFactory context;
@LocalServerPort
private int port = 0;

View File

@@ -0,0 +1,130 @@
package org.springframework.cloud.openfeign.aot;
import java.net.URL;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.ClassName;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FeignChildContextInitializer}.
*
* @author Olga Maciaszek-Sharma
*/
@ExtendWith(OutputCaptureExtension.class)
class FeignChildContextInitializerTests {
private static final Log LOG = LogFactory.getLog(FeignChildContextInitializerTests.class);
@BeforeEach
@AfterEach
void reset() {
ReflectionTestUtils.setField(TomcatURLStreamHandlerFactory.class, "instance", null);
ReflectionTestUtils.setField(URL.class, "factory", null);
}
@Test
@CompileWithForkedClassLoader
@SuppressWarnings("unchecked")
void shouldStartFeignChildContextsFromAotContributions(CapturedOutput output) {
WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(
AnnotationConfigServletWebApplicationContext::new)
.withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class,
FeignAutoConfiguration.class))
.withConfiguration(UserConfigurations.of(TestFeignConfiguration.class));
contextRunner.prepare(context -> {
TestGenerationContext generationContext = new TestGenerationContext(TestTarget.class);
ClassName className = new ApplicationContextAotGenerator().processAheadOfTime(
(GenericApplicationContext) context.getSourceApplicationContext(), generationContext);
generationContext.writeGeneratedContent();
TestCompiler compiler = TestCompiler.forSystem();
compiler.with(generationContext).compile(compiled -> {
ServletWebServerApplicationContext freshApplicationContext = new ServletWebServerApplicationContext();
ApplicationContextInitializer<GenericApplicationContext> initializer = compiled
.getInstance(ApplicationContextInitializer.class, className.toString());
initializer.initialize(freshApplicationContext);
assertThat(output).isNotEmpty();
// TODO
});
});
}
static class TestTarget {
}
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {TestFeignClient.class, TestFeignClientWithConfig.class}, defaultConfiguration = DefaultConfiguration.class)
public static class TestFeignConfiguration {
}
public static class TestConfiguration {
@Bean
TestBean testBean() {
LOG.debug("Instantiating bean from Test custom config");
return new TestBean();
}
}
public static class DefaultConfiguration {
@Bean
TestBean defaultTestBean() {
LOG.debug("Instantiating bean from default custom config");
return new TestBean();
}
}
public static class TestBean {
}
@FeignClient("test")
interface TestFeignClient {
void test();
}
// TODO: verify other annotation parameters
@FeignClient(value = "test-with-config", configuration = TestConfiguration.class)
interface TestFeignClientWithConfig {
void test();
}
}

View File

@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -51,7 +51,7 @@ class PageableEncoderTests {
public static final String SORT_1 = "sort1";
@Autowired
private FeignContext context;
private FeignClientFactory context;
protected String getPageParameter() {
return "page";

View File

@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -52,7 +52,7 @@ class PageableSpringQueryMapEncoderTests {
public static final String SORT_1 = "sort1";
@Autowired
private FeignContext context;
private FeignClientFactory context;
protected String getPageParameter() {
return "page";

View File

@@ -34,7 +34,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -77,7 +77,7 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
class SpringEncoderTests {
@Autowired
private FeignContext context;
private FeignClientFactory context;
@Autowired
@Qualifier("myHttpMessageConverter")