From 24eadd70ed35d3caa95bb9003b9d45007071c74d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 11 Sep 2023 14:25:11 +0200 Subject: [PATCH] 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 --- ...ustomizerBeanRegistrationAotProcessor.java | 5 +-- ...BeanFactoryInitializationAotProcessor.java | 36 ------------------- ...ropertiesBeanRegistrationAotProcessor.java | 10 ++++-- ...leEntriesBeanRegistrationAotProcessor.java | 15 +++++--- 4 files changed, 18 insertions(+), 48 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/ResourceProviderCustomizerBeanRegistrationAotProcessor.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/ResourceProviderCustomizerBeanRegistrationAotProcessor.java index 919cfd3f4e..6bdcccecd3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/ResourceProviderCustomizerBeanRegistrationAotProcessor.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/ResourceProviderCustomizerBeanRegistrationAotProcessor.java @@ -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); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java index 41efc8cd91..3786fc3aca 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessor.java @@ -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> 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. - *

- * 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 { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrationAotProcessor.java index 623626154b..5e96c1ae96 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrationAotProcessor.java @@ -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()) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java index 980928d762..93749dd160 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java @@ -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));