|
|
@ -4,6 +4,7 @@ import java.util.Collection; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.Objects; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
@ -12,21 +13,33 @@ import javax.lang.model.element.Modifier; |
|
|
|
import org.springframework.aot.generate.GeneratedMethod; |
|
|
|
import org.springframework.aot.generate.GeneratedMethod; |
|
|
|
import org.springframework.aot.generate.GenerationContext; |
|
|
|
import org.springframework.aot.generate.GenerationContext; |
|
|
|
import org.springframework.beans.factory.BeanFactory; |
|
|
|
import org.springframework.beans.factory.BeanFactory; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.FactoryBean; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationCode; |
|
|
|
import org.springframework.beans.factory.aot.BeanRegistrationCode; |
|
|
|
|
|
|
|
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.support.AbstractBeanDefinition; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.support.GenericBeanDefinition; |
|
|
|
import org.springframework.beans.factory.support.RegisteredBean; |
|
|
|
import org.springframework.beans.factory.support.RegisteredBean; |
|
|
|
import org.springframework.boot.web.context.WebServerInitializedEvent; |
|
|
|
import org.springframework.boot.web.context.WebServerInitializedEvent; |
|
|
|
import org.springframework.cloud.openfeign.FeignClientFactory; |
|
|
|
import org.springframework.cloud.openfeign.FeignClientFactory; |
|
|
|
|
|
|
|
import org.springframework.cloud.openfeign.FeignClientFactoryBean; |
|
|
|
import org.springframework.cloud.openfeign.FeignClientSpecification; |
|
|
|
import org.springframework.cloud.openfeign.FeignClientSpecification; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
|
|
|
|
import org.springframework.context.ApplicationContextInitializer; |
|
|
|
import org.springframework.context.ApplicationContextInitializer; |
|
|
|
import org.springframework.context.ApplicationListener; |
|
|
|
import org.springframework.context.ApplicationListener; |
|
|
|
import org.springframework.context.ConfigurableApplicationContext; |
|
|
|
import org.springframework.context.ConfigurableApplicationContext; |
|
|
|
import org.springframework.context.aot.ApplicationContextAotGenerator; |
|
|
|
import org.springframework.context.aot.ApplicationContextAotGenerator; |
|
|
|
import org.springframework.context.support.GenericApplicationContext; |
|
|
|
import org.springframework.context.support.GenericApplicationContext; |
|
|
|
import org.springframework.javapoet.ClassName; |
|
|
|
import org.springframework.javapoet.ClassName; |
|
|
|
|
|
|
|
import org.springframework.javapoet.MethodSpec; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
|
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* A {@link BeanRegistrationAotProcessor} that creates an {@link BeanRegistrationAotContribution} for |
|
|
|
* A {@link BeanRegistrationAotProcessor} that creates an {@link BeanRegistrationAotContribution} for |
|
|
@ -35,28 +48,32 @@ import org.springframework.util.Assert; |
|
|
|
* @author Olga Maciaszek-Sharma |
|
|
|
* @author Olga Maciaszek-Sharma |
|
|
|
* @since 4.0.0 |
|
|
|
* @since 4.0.0 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener<WebServerInitializedEvent> { |
|
|
|
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener<WebServerInitializedEvent>, |
|
|
|
|
|
|
|
BeanRegistrationExcludeFilter { |
|
|
|
|
|
|
|
|
|
|
|
private final ApplicationContext applicationContext; |
|
|
|
private final GenericApplicationContext context; |
|
|
|
|
|
|
|
|
|
|
|
private final FeignClientFactory feignClientFactory; |
|
|
|
private final FeignClientFactory feignClientFactory; |
|
|
|
|
|
|
|
|
|
|
|
private final Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers; |
|
|
|
private final Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers; |
|
|
|
|
|
|
|
|
|
|
|
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory) { |
|
|
|
private final Map<String, BeanDefinition> feignClientBeanDefinitions; |
|
|
|
this(applicationContext, feignClientFactory, new HashMap<>()); |
|
|
|
|
|
|
|
|
|
|
|
public FeignChildContextInitializer(GenericApplicationContext context, FeignClientFactory feignClientFactory) { |
|
|
|
|
|
|
|
this(context, feignClientFactory, new HashMap<>()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) { |
|
|
|
public FeignChildContextInitializer(GenericApplicationContext context, FeignClientFactory feignClientFactory, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) { |
|
|
|
this.applicationContext = applicationContext; |
|
|
|
this.context = context; |
|
|
|
this.feignClientFactory = feignClientFactory; |
|
|
|
this.feignClientFactory = feignClientFactory; |
|
|
|
this.applicationContextInitializers = applicationContextInitializers; |
|
|
|
this.applicationContextInitializers = applicationContextInitializers; |
|
|
|
|
|
|
|
feignClientBeanDefinitions = getFeignClientClassNames(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
|
|
|
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
|
|
|
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); |
|
|
|
Assert.isInstanceOf(ConfigurableApplicationContext.class, context); |
|
|
|
ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext); |
|
|
|
ConfigurableApplicationContext context = this.context; |
|
|
|
BeanFactory applicationBeanFactory = context.getBeanFactory(); |
|
|
|
BeanFactory applicationBeanFactory = context.getBeanFactory(); |
|
|
|
if (!(registeredBean.getBeanClass().equals(getClass()) |
|
|
|
if (!(registeredBean.getBeanClass().equals(getClass()) |
|
|
|
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) { |
|
|
|
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) { |
|
|
@ -66,7 +83,7 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso |
|
|
|
Map<String, GenericApplicationContext> childContextAotContributions = contextIds.stream() |
|
|
|
Map<String, GenericApplicationContext> childContextAotContributions = contextIds.stream() |
|
|
|
.map(contextId -> Map.entry(contextId, buildChildContext(contextId))) |
|
|
|
.map(contextId -> Map.entry(contextId, buildChildContext(contextId))) |
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
return new AotContribution(childContextAotContributions); |
|
|
|
return new AotContribution(childContextAotContributions, feignClientBeanDefinitions); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private GenericApplicationContext buildChildContext(String contextId) { |
|
|
|
private GenericApplicationContext buildChildContext(String contextId) { |
|
|
@ -89,13 +106,13 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso |
|
|
|
.forEach(contextId -> convertedInitializers.put(contextId, |
|
|
|
.forEach(contextId -> convertedInitializers.put(contextId, |
|
|
|
(ApplicationContextInitializer<GenericApplicationContext>) applicationContextInitializers |
|
|
|
(ApplicationContextInitializer<GenericApplicationContext>) applicationContextInitializers |
|
|
|
.get(contextId))); |
|
|
|
.get(contextId))); |
|
|
|
return new FeignChildContextInitializer(applicationContext, feignClientFactory, |
|
|
|
return new FeignChildContextInitializer(context, feignClientFactory, |
|
|
|
convertedInitializers); |
|
|
|
convertedInitializers); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onApplicationEvent(WebServerInitializedEvent event) { |
|
|
|
public void onApplicationEvent(WebServerInitializedEvent event) { |
|
|
|
if (applicationContext.equals(event.getApplicationContext())) { |
|
|
|
if (context.equals(event.getApplicationContext())) { |
|
|
|
applicationContextInitializers.keySet().forEach(contextId -> { |
|
|
|
applicationContextInitializers.keySet().forEach(contextId -> { |
|
|
|
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId); |
|
|
|
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId); |
|
|
|
applicationContextInitializers.get(contextId).initialize(childContext); |
|
|
|
applicationContextInitializers.get(contextId).initialize(childContext); |
|
|
@ -110,15 +127,33 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { |
|
|
|
|
|
|
|
return feignClientBeanDefinitions |
|
|
|
|
|
|
|
.containsKey(registeredBean.getBeanClass().getName()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Map<String, BeanDefinition> getFeignClientClassNames() { |
|
|
|
|
|
|
|
Map<String, FeignClientSpecification> configurations = feignClientFactory.getConfigurations(); |
|
|
|
|
|
|
|
return configurations.values().stream() |
|
|
|
|
|
|
|
.map(FeignClientSpecification::getClassName) |
|
|
|
|
|
|
|
.filter(Objects::nonNull) |
|
|
|
|
|
|
|
.filter(className -> !className.equals("default")) |
|
|
|
|
|
|
|
.map(className -> Map.entry(className, context.getBeanDefinition(className))) |
|
|
|
|
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static class AotContribution implements BeanRegistrationAotContribution { |
|
|
|
private static class AotContribution implements BeanRegistrationAotContribution { |
|
|
|
|
|
|
|
|
|
|
|
private final Map<String, GenericApplicationContext> childContexts; |
|
|
|
private final Map<String, GenericApplicationContext> childContexts; |
|
|
|
|
|
|
|
private final Map<String, BeanDefinition> feignClientBeanDefinitions; |
|
|
|
|
|
|
|
|
|
|
|
public AotContribution(Map<String, GenericApplicationContext> childContexts) { |
|
|
|
public AotContribution(Map<String, GenericApplicationContext> childContexts, Map<String, BeanDefinition> feignClientBeanDefinitions) { |
|
|
|
this.childContexts = childContexts.entrySet().stream() |
|
|
|
this.childContexts = childContexts.entrySet().stream() |
|
|
|
.filter(entry -> entry.getValue() != null) |
|
|
|
.filter(entry -> entry.getValue() != null) |
|
|
|
.map(entry -> Map.entry(entry.getKey(), entry.getValue())) |
|
|
|
.map(entry -> Map.entry(entry.getKey(), entry.getValue())) |
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|
|
|
|
|
|
|
this.feignClientBeanDefinitions = feignClientBeanDefinitions; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -149,6 +184,68 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso |
|
|
|
method.addStatement("return instance.withApplicationContextInitializers(initializers)"); |
|
|
|
method.addStatement("return instance.withApplicationContextInitializers(initializers)"); |
|
|
|
}); |
|
|
|
}); |
|
|
|
beanRegistrationCode.addInstancePostProcessor(postProcessorMethod.toMethodReference()); |
|
|
|
beanRegistrationCode.addInstancePostProcessor(postProcessorMethod.toMethodReference()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: ensure the methods are called
|
|
|
|
|
|
|
|
feignClientBeanDefinitions.values().forEach(beanDefinition -> { |
|
|
|
|
|
|
|
// TODO: handle problem retrieving
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
generationContext.getGeneratedClasses() |
|
|
|
|
|
|
|
// FIXME: correct generation context
|
|
|
|
|
|
|
|
.getOrAddForFeatureComponent(registeredBeanDefinition.getBeanClassName(), generatedInitializerClassNames.get(factoryBean.getContextId()), type -> { |
|
|
|
|
|
|
|
type.addMethod(buildMethodSpec(factoryBean, registeredBeanDefinition)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: verify all factory bean method values from registrar!
|
|
|
|
|
|
|
|
private MethodSpec buildMethodSpec(FeignClientFactoryBean registeredFactoryBean, GenericBeanDefinition registeredBeanDefinition) { |
|
|
|
|
|
|
|
String qualifiers = "{\"" + String.join("\",\"", registeredFactoryBean.getQualifiers()) + "\"}"; |
|
|
|
|
|
|
|
return MethodSpec.methodBuilder("feignClientRegistration") |
|
|
|
|
|
|
|
.addJavadoc("registerFeignClient") |
|
|
|
|
|
|
|
.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("definition.setAutowireMode($L)", registeredBeanDefinition.getAutowireMode()) |
|
|
|
|
|
|
|
.addStatement("definition.setLazyInit($L)", registeredBeanDefinition.getLazyInit()) |
|
|
|
|
|
|
|
.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.setPrimary($L)", registeredBeanDefinition.isPrimary()) |
|
|
|
|
|
|
|
.addStatement("String[] qualifiers = new String[]{}") |
|
|
|
|
|
|
|
.addStatement("$T holder = new $T(beanDefinition, \"$L\", new String[]$L)", BeanDefinitionHolder.class, |
|
|
|
|
|
|
|
BeanDefinitionHolder.class, registeredBeanDefinition.getBeanClassName(), qualifiers) |
|
|
|
|
|
|
|
.addStatement("$T.registerBeanDefinition(holder, registry) ", BeanDefinitionReaderUtils.class) |
|
|
|
|
|
|
|
.build(); |
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
// .addStatement("Class<?> beanType = $T.class", Class.forName(feignClientBeanDefinition.getBeanClassName()))
|
|
|
|
|
|
|
|
// .addStatement("$T beanDefinition = new $T(beanType)", RootBeanDefinition.class, RootBeanDefinition.class)
|
|
|
|
|
|
|
|
// .addStatement("beanDefinition.setLazyInit($L)", feignClientBeanDefinition.isLazyInit())
|
|
|
|
|
|
|
|
// .addStatement("beanDefinition.setInstanceSupplier(($T<Object>) registeredBean -> new $T())", InstanceSupplier.class, FeignClientFactoryBean.class)
|
|
|
|
|
|
|
|
// .addStatement("return beanDefinition")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|