Skip to content

Commit

Permalink
Adapt to Spring Framework API change
Browse files Browse the repository at this point in the history
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
  • Loading branch information
snicoll committed Sep 11, 2023
1 parent 14a59a3 commit 24eadd7
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,8 +56,7 @@ protected AotContribution(BeanRegistrationCodeFragments delegate, RegisteredBean

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -63,37 +58,6 @@ public ConfigurationPropertiesReflectionHintsContribution processAheadOfTime(
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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,6 +33,7 @@
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;

/**
Expand Down Expand Up @@ -80,10 +80,14 @@ public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext gener
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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.springframework.boot.jackson;

import java.lang.reflect.Executable;
import java.util.LinkedHashSet;
import java.util.Set;

Expand All @@ -33,6 +32,7 @@
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;

/**
Expand All @@ -54,6 +54,8 @@ public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registe

static class AotContribution extends BeanRegistrationCodeFragmentsDecorator {

private static final Class<?> BEAN_TYPE = JsonMixinModuleEntries.class;

private final RegisteredBean registeredBean;

private final ClassLoader classLoader;
Expand All @@ -64,18 +66,21 @@ static class AotContribution extends BeanRegistrationCodeFragmentsDecorator {
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));
Expand Down

0 comments on commit 24eadd7

Please sign in to comment.