diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactory.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactory.java index 63d8352e..b82e91dc 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactory.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactory.java @@ -16,11 +16,14 @@ package org.springframework.cloud.openfeign; +import java.util.HashMap; import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.cloud.context.named.NamedContextFactory; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.lang.Nullable; /** @@ -35,8 +38,15 @@ import org.springframework.lang.Nullable; */ public class FeignClientFactory extends NamedContextFactory { + private Map> applicationContextInitializers; + public FeignClientFactory() { + this(new HashMap<>()); + } + + public FeignClientFactory(Map> applicationContextInitializers) { super(FeignClientsConfiguration.class, "spring.cloud.openfeign", "spring.cloud.openfeign.client.name"); + this.applicationContextInitializers = applicationContextInitializers; } @Nullable @@ -58,4 +68,26 @@ public class FeignClientFactory extends NamedContextFactory applicationContextInitializers) { + Map> convertedInitializers = new HashMap<>(); + applicationContextInitializers.keySet() + .forEach(contextId -> convertedInitializers.put(contextId, + (ApplicationContextInitializer) applicationContextInitializers + .get(contextId))); + return new FeignClientFactory(convertedInitializers); + } + + public void initializeChildContexts() { + applicationContextInitializers.keySet().forEach(contextId -> { + GenericApplicationContext childContext = buildContext(contextId); + applicationContextInitializers.get(contextId).initialize(childContext); + addContext(contextId, childContext); + childContext.refresh(); + }); + // Ensure the contexts are only initialized once after Aot processing + applicationContextInitializers = new HashMap<>(); + } } + diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index 96589dbf..66825b04 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -115,6 +115,13 @@ public class FeignClientFactoryBean private String[] qualifiers = new String[] {}; + // For AOT testing + public FeignClientFactoryBean() { + if (LOG.isDebugEnabled()) { + LOG.debug("Creating a FeignClientFactoryBean."); + } + } + @Override public void afterPropertiesSet() { Assert.hasText(contextId, "Context id must be set"); @@ -411,9 +418,10 @@ public class FeignClientFactoryBean */ @SuppressWarnings("unchecked") T getTarget() { - FeignClientFactory context = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class) - : applicationContext.getBean(FeignClientFactory.class); - Feign.Builder builder = feign(context); + FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class) + : applicationContext.getBean(FeignClientFactory.class); + feignClientFactory.initializeChildContexts(); + Feign.Builder builder = feign(feignClientFactory); if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) { if (LOG.isInfoEnabled()) { @@ -426,13 +434,13 @@ public class FeignClientFactoryBean url = name; } url += cleanPath(); - return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url)); + return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url)); } if (StringUtils.hasText(url) && !url.startsWith("http")) { url = "http://" + url; } String url = this.url + cleanPath(); - Client client = getOptional(context, Client.class); + Client client = getOptional(feignClientFactory, Client.class); if (client != null) { if (client instanceof FeignBlockingLoadBalancerClient) { // not load balancing because we have a url, @@ -447,10 +455,10 @@ public class FeignClientFactoryBean builder.client(client); } - applyBuildCustomizers(context, builder); + applyBuildCustomizers(feignClientFactory, builder); - Targeter targeter = get(context, Targeter.class); - return targeter.target(this, builder, context, resolveTarget(context, contextId, url)); + Targeter targeter = get(feignClientFactory, Targeter.class); + return targeter.target(this, builder, feignClientFactory, resolveTarget(feignClientFactory, contextId, url)); } private String cleanPath() { diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java index 76bbbc1b..012e51a8 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java @@ -205,11 +205,65 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo @SuppressWarnings("unchecked") private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, - Map attributes) { + Map attributes) { String className = annotationMetadata.getClassName(); - Class clazz = ClassUtils.resolveClassName(className, null); + // TODO: document change and correct AOT and contract usage + if (String.valueOf(false) + .equals(environment.getProperty("spring.cloud.openfeign.lazy-attributes-resolution", String.valueOf(false)))) { + eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry); + } + else { + lazilyRegisterFeignClientBeanDefinition(className, attributes, registry); + } + } + + private void eagerlyRegisterFeignClientBeanDefinition(String className, Map attributes, BeanDefinitionRegistry registry) { + // TODO: verify with prod method + validate(attributes); + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); + validate(attributes); + definition.addPropertyValue("url", getUrl(null, attributes)); + definition.addPropertyValue("path", getPath(null, attributes)); + String name = getName(attributes); + definition.addPropertyValue("name", name); + String contextId = getContextId(null, attributes); + definition.addPropertyValue("contextId", contextId); + definition.addPropertyValue("type", className); + definition.addPropertyValue("dismiss404", Boolean.parseBoolean(String.valueOf(attributes.get("dismiss404")))); + Object fallback = attributes.get("fallback"); + if (fallback != null) { + definition.addPropertyValue("fallback", (fallback instanceof Class ? fallback + : ClassUtils.resolveClassName(fallback.toString(), null))); + } + Object fallbackFactory = attributes.get("fallbackFactory"); + if (fallbackFactory != null) { + definition.addPropertyValue("fallbackFactory", fallbackFactory instanceof Class ? fallbackFactory + : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); + } + definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); + definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); + String[] qualifiers = getQualifiers(attributes); + if (ObjectUtils.isEmpty(qualifiers)) { + qualifiers = new String[] {contextId + "FeignClient"}; + } + // This is done so that there's a way to retrieve qualifiers while generating AOT code + definition.addPropertyValue("qualifiers", qualifiers); + AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); + beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className); + // has a default, won't be null + boolean primary = (Boolean) attributes.get("primary"); + beanDefinition.setPrimary(primary); + BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); + registerRefreshableBeanDefinition(registry, contextId, Request.Options.class, OptionsFactoryBean.class); + registerRefreshableBeanDefinition(registry, contextId, RefreshableUrl.class, RefreshableUrlFactoryBean.class); + } + + private void lazilyRegisterFeignClientBeanDefinition(String className, Map attributes, BeanDefinitionRegistry registry) { + // TODO: verify with prod method ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) registry : null; + Class clazz = ClassUtils.resolveClassName(className, null); String contextId = getContextId(beanFactory, attributes); String name = getName(attributes); FeignClientFactoryBean factoryBean = new FeignClientFactoryBean(); @@ -238,14 +292,13 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo Object fallbackFactory = attributes.get("fallbackFactory"); if (fallbackFactory != null) { factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class) fallbackFactory - : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); + : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); } return factoryBean.getObject(); }); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); definition.setLazyInit(true); validate(attributes); - AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className); beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean); diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java index 7eb23c2d..8ee3b653 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java @@ -16,12 +16,9 @@ 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; @@ -35,22 +32,16 @@ import org.springframework.util.Assert; * @author Olga Maciaszek-Sharma * @since 4.0.0 */ -public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener { +public class FeignChildContextInitializer implements BeanRegistrationAotProcessor { private final ApplicationContext applicationContext; private final FeignClientFactory feignClientFactory; - private final Map> applicationContextInitializers; public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory) { - this(applicationContext, feignClientFactory, new HashMap<>()); - } - - public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory, Map> applicationContextInitializers) { this.applicationContext = applicationContext; this.feignClientFactory = feignClientFactory; - this.applicationContextInitializers = applicationContextInitializers; } @Override @@ -58,7 +49,7 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext); BeanFactory applicationBeanFactory = context.getBeanFactory(); - if (!(registeredBean.getBeanClass().equals(getClass()) + if (!((registeredBean.getBeanClass().equals(FeignClientFactory.class)) && registeredBean.getBeanFactory().equals(applicationBeanFactory))) { return null; } @@ -81,34 +72,6 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso .collect(Collectors.toSet()); } - @SuppressWarnings("unchecked") - public FeignChildContextInitializer withApplicationContextInitializers( - Map applicationContextInitializers) { - Map> convertedInitializers = new HashMap<>(); - applicationContextInitializers.keySet() - .forEach(contextId -> convertedInitializers.put(contextId, - (ApplicationContextInitializer) 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 { @@ -140,8 +103,8 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso method.addJavadoc("Use AOT child context management initialization") .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .addParameter(RegisteredBean.class, "registeredBean") - .addParameter(FeignChildContextInitializer.class, "instance") - .returns(FeignChildContextInitializer.class) + .addParameter(FeignClientFactory.class, "instance") + .returns(FeignClientFactory.class) .addStatement("$T initializers = new $T<>()", Map.class, HashMap.class); generatedInitializerClassNames.keySet() .forEach(contextId -> method.addStatement("initializers.put($S, new $L())", contextId, diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java index d9d6adde..d9512ba6 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java @@ -9,6 +9,8 @@ import javax.lang.model.element.Modifier; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.MethodReference; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; @@ -16,7 +18,6 @@ import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -50,7 +51,8 @@ public class FeignClientBeanFactoryInitializationAotProcessor implements BeanReg @Override public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { - return feignClientBeanDefinitions + return registeredBean.getBeanClass() + .equals(FeignClientFactoryBean.class) || feignClientBeanDefinitions .containsKey(registeredBean.getBeanClass().getName()); } @@ -66,10 +68,11 @@ public class FeignClientBeanFactoryInitializationAotProcessor implements BeanReg @Override public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { - if (!feignClientBeanDefinitions.isEmpty()) { - return new AotContribution(feignClientBeanDefinitions); + BeanFactory applicationBeanFactory = context.getBeanFactory(); + if (feignClientBeanDefinitions.isEmpty() || !beanFactory.equals(applicationBeanFactory)) { + return null; } - return null; + return new AotContribution(feignClientBeanDefinitions); } private static class AotContribution implements BeanFactoryInitializationAotContribution { @@ -88,13 +91,10 @@ public class FeignClientBeanFactoryInitializationAotProcessor implements BeanReg Assert.notNull(beanDefinition, "beanDefinition cannot be null"); Assert.isInstanceOf(GenericBeanDefinition.class, beanDefinition); GenericBeanDefinition registeredBeanDefinition = (GenericBeanDefinition) beanDefinition; - Object factoryBeanObject = registeredBeanDefinition.getAttribute("feignClientsRegistrarFactoryBean"); - Assert.isInstanceOf(FeignClientFactoryBean.class, factoryBeanObject); - FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) factoryBeanObject; - Assert.notNull(factoryBean, "factoryBean cannot be null"); + MutablePropertyValues feignClientProperties = registeredBeanDefinition.getPropertyValues(); return beanFactoryInitializationCode.getMethods() - .add(buildMethodName(factoryBean.getType().getSimpleName()), - method -> generateFeignClientRegistrationMethod(method, factoryBean, registeredBeanDefinition)) + .add(buildMethodName((String) feignClientProperties.get("type")), + method -> generateFeignClientRegistrationMethod(method, feignClientProperties, registeredBeanDefinition)) .getName(); }).collect(Collectors.toSet()); MethodReference initializerMethod = beanFactoryInitializationCode.getMethods() @@ -113,35 +113,32 @@ public class FeignClientBeanFactoryInitializationAotProcessor implements BeanReg feignClientRegistrationMethods.forEach(feignClientRegistrationMethod -> method.addStatement("$N(registry)", feignClientRegistrationMethod)); } - private void generateFeignClientRegistrationMethod(MethodSpec.Builder method, FeignClientFactoryBean registeredFactoryBean, GenericBeanDefinition registeredBeanDefinition) { - String qualifiers = "{\"" + String.join("\",\"", registeredFactoryBean.getQualifiers()) + "\"}"; + private void generateFeignClientRegistrationMethod(MethodSpec.Builder method, MutablePropertyValues feignClientPropertyValues, GenericBeanDefinition registeredBeanDefinition) { + Object feignQualifiers = feignClientPropertyValues.get("qualifiers"); + Assert.notNull(feignQualifiers, "Feign qualifiers cannot be null"); + String qualifiers = "{\"" + String.join("\",\"", (String[]) feignQualifiers) + "\"}"; method - .addJavadoc("register Feign Client: $L", registeredBeanDefinition.getBeanClassName()) + .addJavadoc("register Feign Client: $L", feignClientPropertyValues.get("type")) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(BeanDefinitionRegistry.class, "registry") - .addStatement("Class clazz = $T.resolveClassName(\"$L\", null)", ClassUtils.class, registeredBeanDefinition.getBeanClassName()) - .addStatement("$T beanFactory = registry instanceof $T ? ($T) registry : null", - ConfigurableBeanFactory.class, ConfigurableBeanFactory.class, ConfigurableBeanFactory.class) - .addStatement("$T factoryBean = new $T()", FeignClientFactoryBean.class, FeignClientFactoryBean.class) - .addStatement("factoryBean.setBeanFactory(beanFactory)") - .addStatement("factoryBean.setName(\"$L\")", registeredFactoryBean.getName()) - .addStatement("factoryBean.setContextId(\"$L\")", registeredFactoryBean.getContextId()) - .addStatement("factoryBean.setType($T.class)", registeredFactoryBean.getType()) - .addStatement("factoryBean.setUrl($L)", registeredFactoryBean.getUrl()) - .addStatement("factoryBean.setPath($L)", registeredFactoryBean.getPath()) - .addStatement("factoryBean.setDismiss404($L)", registeredFactoryBean.isDismiss404()) - .addStatement("factoryBean.setFallback($T.class)", registeredFactoryBean.getFallback()) - .addStatement("factoryBean.setFallbackFactory($T.class)", registeredFactoryBean.getFallbackFactory()) - .addStatement("$T definition = $T.genericBeanDefinition(clazz, () -> factoryBean.getObject())", BeanDefinitionBuilder.class, - BeanDefinitionBuilder.class) + .addStatement("Class clazz = $T.resolveClassName(\"$L\", null)", ClassUtils.class, feignClientPropertyValues.get("type")) + .addStatement("$T definition = $T.genericBeanDefinition($T.class)", BeanDefinitionBuilder.class, + BeanDefinitionBuilder.class, FeignClientFactoryBean.class) + .addStatement("definition.addPropertyValue(\"name\",\"$L\")", feignClientPropertyValues.get("name")) + .addStatement("definition.addPropertyValue(\"contextId\", \"$L\")", feignClientPropertyValues.get("contextId")) + .addStatement("definition.addPropertyValue(\"type\", clazz)") + .addStatement("definition.addPropertyValue(\"url\", \"$L\")", feignClientPropertyValues.get("url")) + .addStatement("definition.addPropertyValue(\"path\", \"$L\")", feignClientPropertyValues.get("path")) + .addStatement("definition.addPropertyValue(\"dismiss404\", $L)", feignClientPropertyValues.get("dismiss404")) + .addStatement("definition.addPropertyValue(\"fallback\", $T.class)", feignClientPropertyValues.get("fallback")) + .addStatement("definition.addPropertyValue(\"fallbackFactory\", $T.class)", feignClientPropertyValues.get("fallbackFactory")) .addStatement("definition.setAutowireMode($L)", registeredBeanDefinition.getAutowireMode()) - .addStatement("definition.setLazyInit($L)", registeredBeanDefinition.getLazyInit()) + .addStatement("definition.setLazyInit($L)", registeredBeanDefinition.getLazyInit() != null ? registeredBeanDefinition.getLazyInit() : false) .addStatement("$T beanDefinition = definition.getBeanDefinition()", AbstractBeanDefinition.class) - .addStatement("beanDefinition.setAttribute(\"$L\", $T.class)", FactoryBean.OBJECT_TYPE_ATTRIBUTE, registeredFactoryBean.getType()) - .addStatement("beanDefinition.setAttribute(\"feignClientsRegistrarFactoryBean\", factoryBean)") + .addStatement("beanDefinition.setAttribute(\"$L\", clazz)", FactoryBean.OBJECT_TYPE_ATTRIBUTE) .addStatement("beanDefinition.setPrimary($L)", registeredBeanDefinition.isPrimary()) .addStatement("$T holder = new $T(beanDefinition, \"$L\", new String[]$L)", BeanDefinitionHolder.class, - BeanDefinitionHolder.class, registeredBeanDefinition.getBeanClassName(), qualifiers) + BeanDefinitionHolder.class, feignClientPropertyValues.get("type"), qualifiers) .addStatement("$T.registerBeanDefinition(holder, registry) ", BeanDefinitionReaderUtils.class); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignAotTests.java similarity index 72% rename from spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializerTests.java rename to spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignAotTests.java index fb38e49a..4c7a8107 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignAotTests.java @@ -10,13 +10,16 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.aot.AotDetector; import org.springframework.aot.test.generate.TestGenerationContext; +import org.springframework.beans.factory.annotation.Autowired; 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.test.util.TestPropertyValues; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -31,6 +34,7 @@ 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 org.springframework.web.bind.annotation.GetMapping; import static org.assertj.core.api.Assertions.assertThat; @@ -40,9 +44,9 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Olga Maciaszek-Sharma */ @ExtendWith(OutputCaptureExtension.class) -public class FeignChildContextInitializerTests { +public class FeignAotTests { - private static final Log LOG = LogFactory.getLog(FeignChildContextInitializerTests.class); + private static final Log LOG = LogFactory.getLog(FeignAotTests.class); @BeforeEach @AfterEach @@ -59,7 +63,8 @@ public class FeignChildContextInitializerTests { AnnotationConfigServletWebApplicationContext::new) .withConfiguration(AutoConfigurations.of(ServletWebServerFactoryAutoConfiguration.class, FeignAutoConfiguration.class)) - .withConfiguration(UserConfigurations.of(TestFeignConfiguration.class)); + .withConfiguration(UserConfigurations.of(TestFeignConfiguration.class)) + .withPropertyValues("logging.level.org.springframework.cloud=DEBUG"); contextRunner.prepare(context -> { TestGenerationContext generationContext = new TestGenerationContext(TestTarget.class); ClassName className = new ApplicationContextAotGenerator().processAheadOfTime( @@ -71,9 +76,17 @@ public class FeignChildContextInitializerTests { ApplicationContextInitializer initializer = compiled .getInstance(ApplicationContextInitializer.class, className.toString()); initializer.initialize(freshApplicationContext); - assertThat(output).isNotEmpty(); - - // TODO + assertThat(output).contains("Creating a FeignClientFactoryBean."); + assertThat(output).contains("Refreshing FeignClientFactory-test-with-config", + "Refreshing FeignClientFactory-test"); + assertThat(output).doesNotContain("Instantiating bean from Test custom config", + "Instantiating bean from default custom config"); + TestPropertyValues.of(AotDetector.AOT_ENABLED + "=true") + .applyToSystemProperties(freshApplicationContext::refresh); + assertThat(output).contains("Instantiating bean from Test custom config", + "Instantiating bean from default custom config"); + assertThat(freshApplicationContext.getBean(TestFeignClient.class)).isNotNull(); + assertThat(freshApplicationContext.getBean(TestFeignClientWithConfig.class)).isNotNull(); }); }); } @@ -86,13 +99,21 @@ public class FeignChildContextInitializerTests { @EnableFeignClients(clients = {TestFeignClient.class, TestFeignClientWithConfig.class}, defaultConfiguration = DefaultConfiguration.class) public static class TestFeignConfiguration { + @Autowired + TestFeignClient testFeignClient; + + @Autowired + TestFeignClientWithConfig testFeignClientWithConfig; + } public static class TestConfiguration { @Bean TestBean testBean() { - LOG.debug("Instantiating bean from Test custom config"); + if (LOG.isDebugEnabled()) { + LOG.debug("Instantiating bean from Test custom config"); + } return new TestBean(); } @@ -102,7 +123,9 @@ public class FeignChildContextInitializerTests { @Bean TestBean defaultTestBean() { - LOG.debug("Instantiating bean from default custom config"); + if (LOG.isDebugEnabled()) { + LOG.debug("Instantiating bean from default custom config"); + } return new TestBean(); } @@ -112,9 +135,10 @@ public class FeignChildContextInitializerTests { } - @FeignClient("test") + @FeignClient(value = "test", dismiss404 = true, url = "http://example.com") interface TestFeignClient { + @GetMapping void test(); } @@ -123,6 +147,7 @@ public class FeignChildContextInitializerTests { @FeignClient(value = "test-with-config", configuration = TestConfiguration.class) interface TestFeignClientWithConfig { + @GetMapping void test(); }