Browse Source

Move changes over to a BeanFactoryInitializationAotProcessor.

aot-support
Olga Maciaszek-Sharma 2 years ago
parent
commit
6a833cd3db
  1. 8
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
  2. 123
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java
  3. 149
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java

8
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

@ -56,12 +56,12 @@ import org.springframework.cloud.client.actuator.HasFeatures; @@ -56,12 +56,12 @@ 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.aot.FeignClientBeanFactoryInitializationAotProcessor;
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;
@ -117,6 +117,12 @@ public class FeignAutoConfiguration { @@ -117,6 +117,12 @@ public class FeignAutoConfiguration {
return new FeignChildContextInitializer(parentContext, feignClientFactory);
}
@Bean
static FeignClientBeanFactoryInitializationAotProcessor feignClientBeanFactoryInitializationCodeGenerator(GenericApplicationContext applicationContext,
FeignClientFactory feignClientFactory) {
return new FeignClientBeanFactoryInitializationAotProcessor(applicationContext, feignClientFactory);
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.openfeign.cache.enabled", matchIfMissing = true)
@ConditionalOnBean(CacheInterceptor.class)

123
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignChildContextInitializer.java

@ -4,7 +4,6 @@ import java.util.Collection; @@ -4,7 +4,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@ -13,33 +12,21 @@ import javax.lang.model.element.Modifier; @@ -13,33 +12,21 @@ 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.FactoryBean;
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.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.boot.web.context.WebServerInitializedEvent;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
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.javapoet.MethodSpec;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A {@link BeanRegistrationAotProcessor} that creates an {@link BeanRegistrationAotContribution} for
@ -48,32 +35,28 @@ import org.springframework.util.ClassUtils; @@ -48,32 +35,28 @@ import org.springframework.util.ClassUtils;
* @author Olga Maciaszek-Sharma
* @since 4.0.0
*/
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener<WebServerInitializedEvent>,
BeanRegistrationExcludeFilter {
public class FeignChildContextInitializer implements BeanRegistrationAotProcessor, ApplicationListener<WebServerInitializedEvent> {
private final GenericApplicationContext context;
private final ApplicationContext applicationContext;
private final FeignClientFactory feignClientFactory;
private final Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers;
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
public FeignChildContextInitializer(GenericApplicationContext context, FeignClientFactory feignClientFactory) {
this(context, feignClientFactory, new HashMap<>());
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory) {
this(applicationContext, feignClientFactory, new HashMap<>());
}
public FeignChildContextInitializer(GenericApplicationContext context, FeignClientFactory feignClientFactory, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) {
this.context = context;
public FeignChildContextInitializer(ApplicationContext applicationContext, FeignClientFactory feignClientFactory, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) {
this.applicationContext = applicationContext;
this.feignClientFactory = feignClientFactory;
this.applicationContextInitializers = applicationContextInitializers;
feignClientBeanDefinitions = getFeignClientClassNames();
}
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, context);
ConfigurableApplicationContext context = this.context;
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
ConfigurableApplicationContext context = ((ConfigurableApplicationContext) applicationContext);
BeanFactory applicationBeanFactory = context.getBeanFactory();
if (!(registeredBean.getBeanClass().equals(getClass())
&& registeredBean.getBeanFactory().equals(applicationBeanFactory))) {
@ -83,7 +66,7 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso @@ -83,7 +66,7 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso
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, feignClientBeanDefinitions);
return new AotContribution(childContextAotContributions);
}
private GenericApplicationContext buildChildContext(String contextId) {
@ -106,13 +89,13 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso @@ -106,13 +89,13 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso
.forEach(contextId -> convertedInitializers.put(contextId,
(ApplicationContextInitializer<GenericApplicationContext>) applicationContextInitializers
.get(contextId)));
return new FeignChildContextInitializer(context, feignClientFactory,
return new FeignChildContextInitializer(applicationContext, feignClientFactory,
convertedInitializers);
}
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
if (context.equals(event.getApplicationContext())) {
if (applicationContext.equals(event.getApplicationContext())) {
applicationContextInitializers.keySet().forEach(contextId -> {
GenericApplicationContext childContext = feignClientFactory.buildContext(contextId);
applicationContextInitializers.get(contextId).initialize(childContext);
@ -127,33 +110,15 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso @@ -127,33 +110,15 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso
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 final Map<String, GenericApplicationContext> childContexts;
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
public AotContribution(Map<String, GenericApplicationContext> childContexts, Map<String, BeanDefinition> feignClientBeanDefinitions) {
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));
this.feignClientBeanDefinitions = feignClientBeanDefinitions;
}
@Override
@ -184,68 +149,6 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso @@ -184,68 +149,6 @@ public class FeignChildContextInitializer implements BeanRegistrationAotProcesso
method.addStatement("return instance.withApplicationContextInitializers(initializers)");
});
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")
}
}

149
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java

@ -0,0 +1,149 @@ @@ -0,0 +1,149 @@
package org.springframework.cloud.openfeign.aot;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.lang.model.element.Modifier;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
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;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.cloud.openfeign.FeignClientSpecification;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.MethodSpec;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* @author Olga Maciaszek-Sharma
*/
public class FeignClientBeanFactoryInitializationAotProcessor implements BeanRegistrationExcludeFilter, BeanFactoryInitializationAotProcessor {
private final GenericApplicationContext context;
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
public FeignClientBeanFactoryInitializationAotProcessor(GenericApplicationContext context, FeignClientFactory feignClientFactory) {
this.context = context;
this.feignClientBeanDefinitions = getFeignClientBeanDefinitions(feignClientFactory);
}
@Override
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
return feignClientBeanDefinitions
.containsKey(registeredBean.getBeanClass().getName());
}
private Map<String, BeanDefinition> getFeignClientBeanDefinitions(FeignClientFactory feignClientFactory) {
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));
}
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
if (!feignClientBeanDefinitions.isEmpty()) {
return new AotContribution(feignClientBeanDefinitions);
}
return null;
}
private static class AotContribution implements BeanFactoryInitializationAotContribution {
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
private AotContribution(Map<String, BeanDefinition> feignClientBeanDefinitions) {
this.feignClientBeanDefinitions = feignClientBeanDefinitions;
}
@Override
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
Set<String> feignClientRegistrationMethods = feignClientBeanDefinitions.values()
.stream()
.map(beanDefinition -> {
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");
return beanFactoryInitializationCode.getMethods()
.add(buildMethodName(factoryBean.getType().getSimpleName()),
method -> generateFeignClientRegistrationMethod(method, factoryBean, registeredBeanDefinition))
.getName();
}).collect(Collectors.toSet());
MethodReference initializerMethod = beanFactoryInitializationCode.getMethods()
.add("initialize", method -> generateInitializerMethod(method, feignClientRegistrationMethods))
.toMethodReference();
beanFactoryInitializationCode.addInitializer(initializerMethod);
}
private String buildMethodName(String clientName) {
return "register" + clientName + "FeignClient";
}
private void generateInitializerMethod(MethodSpec.Builder method, Set<String> feignClientRegistrationMethods) {
method.addModifiers(Modifier.PUBLIC);
method.addParameter(DefaultListableBeanFactory.class, "registry");
feignClientRegistrationMethods.forEach(feignClientRegistrationMethod -> method.addStatement("$N(registry)", feignClientRegistrationMethod));
}
private void generateFeignClientRegistrationMethod(MethodSpec.Builder method, FeignClientFactoryBean registeredFactoryBean, GenericBeanDefinition registeredBeanDefinition) {
String qualifiers = "{\"" + String.join("\",\"", registeredFactoryBean.getQualifiers()) + "\"}";
method
.addJavadoc("register Feign Client: $L", registeredBeanDefinition.getBeanClassName())
.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("$T holder = new $T(beanDefinition, \"$L\", new String[]$L)", BeanDefinitionHolder.class,
BeanDefinitionHolder.class, registeredBeanDefinition.getBeanClassName(), qualifiers)
.addStatement("$T.registerBeanDefinition(holder, registry) ", BeanDefinitionReaderUtils.class);
}
}
}
Loading…
Cancel
Save