Rename BeanInstantiationContributor to Contribution

This commit polishes the contribution model where an AOT contributing
bean post processor can return a contribution, rather than a
contributor. This makes it easier to return `null` if no contribution
can be produced now that it is named this way.

See gh-28047
This commit is contained in:
Stephane Nicoll 2022-03-04 09:50:27 +01:00
parent 572d017370
commit 20b17f02a2
6 changed files with 51 additions and 52 deletions

View File

@ -57,7 +57,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor;
import org.springframework.beans.factory.generator.BeanInstantiationContributor; import org.springframework.beans.factory.generator.BeanInstantiationContribution;
import org.springframework.beans.factory.generator.InjectionGenerator; import org.springframework.beans.factory.generator.InjectionGenerator;
import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
@ -268,12 +268,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
@Override @Override
public BeanInstantiationContributor buildAotContributor(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, beanDefinition); InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, beanDefinition);
Collection<InjectedElement> injectedElements = metadata.getInjectedElements(); Collection<InjectedElement> injectedElements = metadata.getInjectedElements();
return (!ObjectUtils.isEmpty(injectedElements) return (!ObjectUtils.isEmpty(injectedElements)
? new AutowiredAnnotationBeanInstantiationContributor(injectedElements) ? new AutowiredAnnotationBeanInstantiationContribution(injectedElements)
: BeanInstantiationContributor.NO_OP); : null);
} }
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) { private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) {
@ -821,19 +821,19 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
} }
private static final class AutowiredAnnotationBeanInstantiationContributor implements BeanInstantiationContributor { private static final class AutowiredAnnotationBeanInstantiationContribution implements BeanInstantiationContribution {
private final Collection<InjectedElement> injectedElements; private final Collection<InjectedElement> injectedElements;
private final InjectionGenerator generator; private final InjectionGenerator generator;
AutowiredAnnotationBeanInstantiationContributor(Collection<InjectedElement> injectedElements) { AutowiredAnnotationBeanInstantiationContribution(Collection<InjectedElement> injectedElements) {
this.injectedElements = injectedElements; this.injectedElements = injectedElements;
this.generator = new InjectionGenerator(); this.generator = new InjectionGenerator();
} }
@Override @Override
public void contribute(CodeContribution contribution) { public void applyTo(CodeContribution contribution) {
this.injectedElements.forEach(element -> { this.injectedElements.forEach(element -> {
boolean isRequired = isRequired(element); boolean isRequired = isRequired(element);
Member member = element.getMember(); Member member = element.getMember();

View File

@ -18,6 +18,7 @@ package org.springframework.beans.factory.generator;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.lang.Nullable;
/** /**
* Specialization of {@link BeanPostProcessor} that contributes to bean * Specialization of {@link BeanPostProcessor} that contributes to bean
@ -35,12 +36,14 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
public interface AotContributingBeanPostProcessor extends BeanPostProcessor { public interface AotContributingBeanPostProcessor extends BeanPostProcessor {
/** /**
* Build a {@link BeanInstantiationContributor} for the given bean definition. * Contribute a {@link BeanInstantiationContribution} for the given bean definition,
* if applicable.
* @param beanDefinition the merged bean definition for the bean * @param beanDefinition the merged bean definition for the bean
* @param beanType the inferred type of the bean * @param beanType the inferred type of the bean
* @param beanName the name of the bean * @param beanName the name of the bean
* @return the contributor to use * @return the contribution to use or {@code null} if the bean should not be processed
*/ */
BeanInstantiationContributor buildAotContributor(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName); @Nullable
BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
} }

View File

@ -19,24 +19,18 @@ package org.springframework.beans.factory.generator;
import org.springframework.aot.generator.CodeContribution; import org.springframework.aot.generator.CodeContribution;
/** /**
* Contributor to the code that instantiates a bean following ahead of time * A contribution to the instantiation of a bean following ahead of time
* processing. * processing.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 6.0 * @since 6.0
*/ */
@FunctionalInterface @FunctionalInterface
public interface BeanInstantiationContributor { public interface BeanInstantiationContribution {
/** /**
* A {@link BeanInstantiationContributor} that does not contribute anything * Contribute bean instantiation to the specified {@link CodeContribution}.
* to the {@link CodeContribution}. * <p>Implementations of this interface can assume the following variables
*/
BeanInstantiationContributor NO_OP = contribution -> { };
/**
* Contribute to the specified {@link CodeContribution}.
* <p>Implementation of this interface can assume the following variables
* to be accessible: * to be accessible:
* <ul> * <ul>
* <li>{@code beanFactory}: the general {@code DefaultListableBeanFactory}</li> * <li>{@code beanFactory}: the general {@code DefaultListableBeanFactory}</li>
@ -45,6 +39,6 @@ public interface BeanInstantiationContributor {
* </ul> * </ul>
* @param contribution the {@link CodeContribution} to use * @param contribution the {@link CodeContribution} to use
*/ */
void contribute(CodeContribution contribution); void applyTo(CodeContribution contribution);
} }

View File

@ -36,25 +36,25 @@ import org.springframework.util.ClassUtils;
* Write the necessary statements to instantiate a bean. * Write the necessary statements to instantiate a bean.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @see BeanInstantiationContributor * @see BeanInstantiationContribution
*/ */
class DefaultBeanInstantiationGenerator { class DefaultBeanInstantiationGenerator {
private final Executable instanceCreator; private final Executable instanceCreator;
private final List<BeanInstantiationContributor> contributors; private final List<BeanInstantiationContribution> contributions;
private final InjectionGenerator injectionGenerator; private final InjectionGenerator injectionGenerator;
private final Options beanInstanceOptions; private final Options beanInstanceOptions;
DefaultBeanInstantiationGenerator(Executable instanceCreator, List<BeanInstantiationContributor> contributors) { DefaultBeanInstantiationGenerator(Executable instanceCreator, List<BeanInstantiationContribution> contributions) {
this.instanceCreator = instanceCreator; this.instanceCreator = instanceCreator;
this.contributors = List.copyOf(contributors); this.contributions = List.copyOf(contributions);
this.injectionGenerator = new InjectionGenerator(); this.injectionGenerator = new InjectionGenerator();
this.beanInstanceOptions = Options.defaults().useReflection(member -> false) this.beanInstanceOptions = Options.defaults().useReflection(member -> false)
.assignReturnType(member -> !this.contributors.isEmpty()).build(); .assignReturnType(member -> !this.contributions.isEmpty()).build();
} }
/** /**
@ -78,7 +78,7 @@ class DefaultBeanInstantiationGenerator {
private void writeBeanInstantiation(CodeContribution contribution, Constructor<?> constructor) { private void writeBeanInstantiation(CodeContribution contribution, Constructor<?> constructor) {
Class<?> declaringType = ClassUtils.getUserClass(constructor.getDeclaringClass()); Class<?> declaringType = ClassUtils.getUserClass(constructor.getDeclaringClass());
boolean innerClass = isInnerClass(declaringType); boolean innerClass = isInnerClass(declaringType);
boolean multiStatements = !this.contributors.isEmpty(); boolean multiStatements = !this.contributions.isEmpty();
int minArgs = isInnerClass(declaringType) ? 2 : 1; int minArgs = isInnerClass(declaringType) ? 2 : 1;
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
// Shortcut for common case // Shortcut for common case
@ -110,8 +110,8 @@ class DefaultBeanInstantiationGenerator {
contribution.statements().addStatement(code.build()); contribution.statements().addStatement(code.build());
if (multiStatements) { if (multiStatements) {
for (BeanInstantiationContributor contributor : this.contributors) { for (BeanInstantiationContribution contributor : this.contributions) {
contributor.contribute(contribution); contributor.applyTo(contribution);
} }
contribution.statements().addStatement("return bean") contribution.statements().addStatement("return bean")
.add(codeBlock -> codeBlock.unindent().add("}")); .add(codeBlock -> codeBlock.unindent().add("}"));
@ -127,7 +127,7 @@ class DefaultBeanInstantiationGenerator {
contribution.runtimeHints().reflection().registerMethod(method, contribution.runtimeHints().reflection().registerMethod(method,
hint -> hint.withMode(ExecutableMode.INTROSPECT)); hint -> hint.withMode(ExecutableMode.INTROSPECT));
List<Class<?>> parameterTypes = new ArrayList<>(Arrays.asList(method.getParameterTypes())); List<Class<?>> parameterTypes = new ArrayList<>(Arrays.asList(method.getParameterTypes()));
boolean multiStatements = !this.contributors.isEmpty(); boolean multiStatements = !this.contributions.isEmpty();
Class<?> declaringType = method.getDeclaringClass(); Class<?> declaringType = method.getDeclaringClass();
CodeBlock.Builder code = CodeBlock.builder(); CodeBlock.Builder code = CodeBlock.builder();
// Shortcut for common case // Shortcut for common case
@ -148,8 +148,8 @@ class DefaultBeanInstantiationGenerator {
code.add(this.injectionGenerator.writeInstantiation(method)); code.add(this.injectionGenerator.writeInstantiation(method));
contribution.statements().addStatement(code.build()); contribution.statements().addStatement(code.build());
if (multiStatements) { if (multiStatements) {
for (BeanInstantiationContributor contributor : this.contributors) { for (BeanInstantiationContribution contributor : this.contributions) {
contributor.contribute(contribution); contributor.applyTo(contribution);
} }
contribution.statements().addStatement("return bean") contribution.statements().addStatement("return bean")
.add(codeBlock -> codeBlock.unindent().add("}")); .add(codeBlock -> codeBlock.unindent().add("}"));

View File

@ -24,11 +24,12 @@ import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean;
import org.springframework.beans.factory.generator.BeanInstantiationContributor; import org.springframework.beans.factory.generator.BeanInstantiationContribution;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.javapoet.support.CodeSnippet; import org.springframework.javapoet.support.CodeSnippet;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -37,10 +38,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
class AutowiredAnnotationBeanInstantiationContributorTests { class AutowiredAnnotationBeanInstantiationContributionTests {
@Test @Test
void buildAotContributorWithPackageProtectedFieldInjection() { void contributeWithPackageProtectedFieldInjection() {
CodeContribution contribution = contribute(PackageProtectedFieldInjectionSample.class); CodeContribution contribution = contribute(PackageProtectedFieldInjectionSample.class);
assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo("""
instanceContext.field("environment", Environment.class) instanceContext.field("environment", Environment.class)
@ -58,12 +59,12 @@ class AutowiredAnnotationBeanInstantiationContributorTests {
} }
@Test @Test
void buildAotContributorWithPrivateFieldInjection() { void contributeWithPrivateFieldInjection() {
CodeContribution contribution = contribute(PrivateFieldInjectionSample.class); CodeContribution contribution = contribute(PrivateFieldInjectionSample.class);
assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo("""
instanceContext.field("environment", Environment.class) instanceContext.field("environment", Environment.class)
.invoke(beanFactory, (attributes) -> { .invoke(beanFactory, (attributes) -> {
Field environmentField = ReflectionUtils.findField(AutowiredAnnotationBeanInstantiationContributorTests.PrivateFieldInjectionSample.class, "environment", Environment.class); Field environmentField = ReflectionUtils.findField(AutowiredAnnotationBeanInstantiationContributionTests.PrivateFieldInjectionSample.class, "environment", Environment.class);
ReflectionUtils.makeAccessible(environmentField); ReflectionUtils.makeAccessible(environmentField);
ReflectionUtils.setField(environmentField, bean, attributes.get(0)); ReflectionUtils.setField(environmentField, bean, attributes.get(0));
})"""); })""");
@ -79,7 +80,7 @@ class AutowiredAnnotationBeanInstantiationContributorTests {
} }
@Test @Test
void buildAotContributorWithPublicMethodInjection() { void contributeWithPublicMethodInjection() {
CodeContribution contribution = contribute(PublicMethodInjectionSample.class); CodeContribution contribution = contribute(PublicMethodInjectionSample.class);
assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo("""
instanceContext.method("setTestBean", TestBean.class) instanceContext.method("setTestBean", TestBean.class)
@ -95,7 +96,7 @@ class AutowiredAnnotationBeanInstantiationContributorTests {
} }
@Test @Test
void buildAotContributorWithInjectionPoints() { void contributeWithInjectionPoints() {
CodeContribution contribution = contribute(ResourceInjectionBean.class); CodeContribution contribution = contribute(ResourceInjectionBean.class);
assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo("""
instanceContext.field("testBean", TestBean.class) instanceContext.field("testBean", TestBean.class)
@ -116,23 +117,24 @@ class AutowiredAnnotationBeanInstantiationContributorTests {
} }
@Test @Test
void buildAotContributorWithoutInjectionPoints() { void contributeWithoutInjectionPoints() {
BeanInstantiationContributor contributor = createAotContributor(String.class); BeanInstantiationContribution contributor = createContribution(String.class);
assertThat(contributor).isNotNull().isSameAs(BeanInstantiationContributor.NO_OP); assertThat(contributor).isNull();
} }
private DefaultCodeContribution contribute(Class<?> type) { private DefaultCodeContribution contribute(Class<?> type) {
BeanInstantiationContributor contributor = createAotContributor(type); BeanInstantiationContribution contributor = createContribution(type);
assertThat(contributor).isNotNull(); assertThat(contributor).isNotNull();
DefaultCodeContribution contribution = new DefaultCodeContribution(new RuntimeHints()); DefaultCodeContribution contribution = new DefaultCodeContribution(new RuntimeHints());
contributor.contribute(contribution); contributor.applyTo(contribution);
return contribution; return contribution;
} }
private BeanInstantiationContributor createAotContributor(Class<?> type) { @Nullable
private BeanInstantiationContribution createContribution(Class<?> type) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
RootBeanDefinition beanDefinition = new RootBeanDefinition(type); RootBeanDefinition beanDefinition = new RootBeanDefinition(type);
return bpp.buildAotContributor(beanDefinition, type, "test"); return bpp.contribute(beanDefinition, type, "test");
} }

View File

@ -106,10 +106,10 @@ class DefaultBeanInstantiationGeneratorTests {
} }
@Test @Test
void generateUsingNoArgConstructorAndContributorsDoesNotUseMethodReference() { void generateUsingNoArgConstructorAndContributionsDoesNotUseMethodReference() {
CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0], CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0],
contrib -> contrib.statements().add(CodeBlock.of("// hello\n")), contrib -> contrib.statements().add(CodeBlock.of("// hello\n")),
BeanInstantiationContributor.NO_OP); contrib -> {});
assertThat(code(contribution)).isEqualTo(""" assertThat(code(contribution)).isEqualTo("""
(instanceContext) -> { (instanceContext) -> {
SimpleConfiguration bean = new SimpleConfiguration(); SimpleConfiguration bean = new SimpleConfiguration();
@ -119,7 +119,7 @@ class DefaultBeanInstantiationGeneratorTests {
} }
@Test @Test
void generateUsingContributorsRegisterHints() { void generateUsingContributionsRegisterHints() {
CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0], CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0],
contrib -> { contrib -> {
contrib.statements().add(CodeBlock.of("// hello\n")); contrib.statements().add(CodeBlock.of("// hello\n"));
@ -170,7 +170,7 @@ class DefaultBeanInstantiationGeneratorTests {
} }
@Test @Test
void generateUsingMethodAndContributors() { void generateUsingMethodAndContributions() {
CodeContribution contribution = generate(method(SimpleConfiguration.class, "stringBean"), CodeContribution contribution = generate(method(SimpleConfiguration.class, "stringBean"),
contrib -> { contrib -> {
contrib.statements().add(CodeBlock.of("// hello\n")); contrib.statements().add(CodeBlock.of("// hello\n"));
@ -240,9 +240,9 @@ class DefaultBeanInstantiationGeneratorTests {
} }
private CodeContribution generate(Executable executable, private CodeContribution generate(Executable executable,
BeanInstantiationContributor... beanInstantiationContributors) { BeanInstantiationContribution... beanInstantiationContributions) {
DefaultBeanInstantiationGenerator generator = new DefaultBeanInstantiationGenerator(executable, DefaultBeanInstantiationGenerator generator = new DefaultBeanInstantiationGenerator(executable,
Arrays.asList(beanInstantiationContributors)); Arrays.asList(beanInstantiationContributions));
return generator.generateBeanInstantiation(new RuntimeHints()); return generator.generateBeanInstantiation(new RuntimeHints());
} }