Adapt to Spring Framework API change

This commit adapts to API changes in Spring Framework, see
spring-projects/spring-framework#31117

Previously, the "autowired" executable to use for a bean was always
resolved, even if a custom code fragment didn't really need it. This
is key for binding of immutable configuration properties as we use an
instance supplier for it.

This changes means that the workaround added in maintenance releases
can be removed.

See gh-37337
This commit is contained in:
Stephane Nicoll
2023-09-11 14:25:11 +02:00
parent 14a59a33dc
commit 24eadd70ed
4 changed files with 18 additions and 48 deletions

View File

@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.flyway;
import java.lang.reflect.Executable;
import javax.lang.model.element.Modifier;
import org.springframework.aot.generate.GeneratedMethod;
@@ -58,8 +56,7 @@ class ResourceProviderCustomizerBeanRegistrationAotProcessor implements BeanRegi
@Override
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
boolean allowDirectSupplierShortcut) {
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
GeneratedMethod generatedMethod = beanRegistrationCode.getMethods().add("getInstance", (method) -> {
method.addJavadoc("Get the bean instance for '$L'.", this.registeredBean.getBeanName());
method.addModifiers(Modifier.PRIVATE, Modifier.STATIC);

View File

@@ -16,18 +16,14 @@
package org.springframework.boot.context.properties;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.beans.BeansException;
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.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
import org.springframework.boot.context.properties.bind.BindMethod;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrar;
@@ -47,7 +43,6 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements Be
@Override
public ConfigurationPropertiesReflectionHintsContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new BindConstructorAwareBeanPostProcessor(beanFactory));
String[] beanNames = beanFactory.getBeanNamesForAnnotation(ConfigurationProperties.class);
List<Bindable<?>> bindables = new ArrayList<>();
for (String beanName : beanNames) {
@@ -63,37 +58,6 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessor implements Be
return (!bindables.isEmpty()) ? new ConfigurationPropertiesReflectionHintsContribution(bindables) : null;
}
/**
* {@link SmartInstantiationAwareBeanPostProcessor} implementation to work around
* framework's constructor resolver for immutable configuration properties.
* <p>
* Constructor binding supports multiple constructors as long as one is identified as
* the candidate for binding. Unfortunately, framework is not aware of such feature
* and attempts to resolve the autowired constructor to use.
*/
static class BindConstructorAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
private final ConfigurableListableBeanFactory beanFactory;
BindConstructorAwareBeanPostProcessor(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
BindMethod bindMethod = BindMethodAttribute.get(this.beanFactory, beanName);
if (bindMethod != null && bindMethod == BindMethod.VALUE_OBJECT) {
Constructor<?> bindConstructor = BindConstructorProvider.DEFAULT.getBindConstructor(beanClass, false);
if (bindConstructor != null) {
return new Constructor<?>[] { bindConstructor };
}
}
return null;
}
}
static final class ConfigurationPropertiesReflectionHintsContribution
implements BeanFactoryInitializationAotContribution {

View File

@@ -16,7 +16,6 @@
package org.springframework.boot.context.properties;
import java.lang.reflect.Executable;
import java.util.function.Predicate;
import javax.lang.model.element.Modifier;
@@ -34,6 +33,7 @@ import org.springframework.beans.factory.support.InstanceSupplier;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.properties.bind.BindMethod;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
/**
@@ -80,10 +80,14 @@ class ConfigurationPropertiesBeanRegistrationAotProcessor implements BeanRegistr
beanDefinition, attributeFilter.or(BindMethodAttribute.NAME::equals));
}
@Override
public ClassName getTarget(RegisteredBean registeredBean) {
return ClassName.get(this.registeredBean.getBeanClass());
}
@Override
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
boolean allowDirectSupplierShortcut) {
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
GeneratedMethod generatedMethod = beanRegistrationCode.getMethods().add("getInstance", (method) -> {
Class<?> beanClass = this.registeredBean.getBeanClass();
method.addJavadoc("Get the bean instance for '$L'.", this.registeredBean.getBeanName())

View File

@@ -16,7 +16,6 @@
package org.springframework.boot.jackson;
import java.lang.reflect.Executable;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -33,6 +32,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragments;
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragmentsDecorator;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
/**
@@ -54,6 +54,8 @@ class JsonMixinModuleEntriesBeanRegistrationAotProcessor implements BeanRegistra
static class AotContribution extends BeanRegistrationCodeFragmentsDecorator {
private static final Class<?> BEAN_TYPE = JsonMixinModuleEntries.class;
private final RegisteredBean registeredBean;
private final ClassLoader classLoader;
@@ -64,18 +66,21 @@ class JsonMixinModuleEntriesBeanRegistrationAotProcessor implements BeanRegistra
this.classLoader = registeredBean.getBeanFactory().getBeanClassLoader();
}
@Override
public ClassName getTarget(RegisteredBean registeredBean) {
return ClassName.get(BEAN_TYPE);
}
@Override
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
boolean allowDirectSupplierShortcut) {
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
JsonMixinModuleEntries entries = this.registeredBean.getBeanFactory()
.getBean(this.registeredBean.getBeanName(), JsonMixinModuleEntries.class);
contributeHints(generationContext.getRuntimeHints(), entries);
GeneratedMethod generatedMethod = beanRegistrationCode.getMethods().add("getInstance", (method) -> {
Class<?> beanType = JsonMixinModuleEntries.class;
method.addJavadoc("Get the bean instance for '$L'.", this.registeredBean.getBeanName());
method.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
method.returns(beanType);
method.returns(BEAN_TYPE);
CodeBlock.Builder code = CodeBlock.builder();
code.add("return $T.create(", JsonMixinModuleEntries.class).beginControlFlow("(mixins) ->");
entries.doWithEntry(this.classLoader, (type, mixin) -> addEntryCode(code, type, mixin));