From 1742e121e79830e38ac902496a27fc0dd807d4f5 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 10 Mar 2022 16:06:52 +0100 Subject: [PATCH] Update bean registration contribution tests to use TestCompiler --- spring-beans/spring-beans.gradle | 1 + ...istrationBeanFactoryContributionTests.java | 255 ++++++++++-------- .../generator/BeanFactoryInitializer.java | 26 ++ 3 files changed, 172 insertions(+), 110 deletions(-) create mode 100644 spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/generator/BeanFactoryInitializer.java diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index 6b8f69ff30e..877d28852dc 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -10,6 +10,7 @@ dependencies { optional("org.jetbrains.kotlin:kotlin-reflect") optional("org.jetbrains.kotlin:kotlin-stdlib") testImplementation(testFixtures(project(":spring-core"))) + testImplementation(project(":spring-core-test")) testImplementation("jakarta.annotation:jakarta.annotation-api") testFixturesApi("org.junit.jupiter:junit-jupiter-api") testFixturesImplementation("org.assertj:assertj-core") diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java index 064973afb01..689d7360a71 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java @@ -25,8 +25,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; +import javax.lang.model.element.Modifier; + import org.junit.jupiter.api.Test; import org.springframework.aot.generator.DefaultGeneratedTypeContext; @@ -36,16 +39,18 @@ import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; -import org.springframework.beans.MutablePropertyValues; +import org.springframework.aot.test.generator.compile.TestCompiler; +import org.springframework.aot.test.generator.file.SourceFile; +import org.springframework.aot.test.generator.file.SourceFiles; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.factory.generator.BeanFactoryInitializer; import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent; import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent; import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration; @@ -59,6 +64,8 @@ import org.springframework.core.testfixture.aot.generator.visibility.PublicFacto import org.springframework.javapoet.ClassName; import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.CodeBlock.Builder; +import org.springframework.javapoet.JavaFile; +import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.support.CodeSnippet; import org.springframework.javapoet.support.MultiStatement; import org.springframework.util.ReflectionUtils; @@ -143,7 +150,7 @@ class BeanRegistrationBeanFactoryContributionTests { @Test void generateUsingPublicAccessDoesNotAccessAnotherPackage() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition(); - getContribution(beanDefinition, singleConstructor(SimpleConfiguration.class)).applyTo(this.initialization); + getContributionFor(beanDefinition, singleConstructor(SimpleConfiguration.class)).applyTo(this.initialization); assertThat(this.generatedTypeContext.toJavaFiles()).hasSize(1); assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo(""" BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) @@ -154,7 +161,7 @@ class BeanRegistrationBeanFactoryContributionTests { @Test void generateUsingProtectedConstructorWritesToBlessedPackage() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ProtectedConstructorComponent.class).getBeanDefinition(); - getContribution(beanDefinition, singleConstructor(ProtectedConstructorComponent.class)).applyTo(this.initialization); + getContributionFor(beanDefinition, singleConstructor(ProtectedConstructorComponent.class)).applyTo(this.initialization); assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedConstructorComponent.class.getPackageName())).isTrue(); GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName()); assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence(""" @@ -169,7 +176,7 @@ class BeanRegistrationBeanFactoryContributionTests { @Test void generateUsingProtectedFactoryMethodWritesToBlessedPackage() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition(); - getContribution(beanDefinition, method(ProtectedFactoryMethod.class, "testBean", Integer.class)) + getContributionFor(beanDefinition, method(ProtectedFactoryMethod.class, "testBean", Integer.class)) .applyTo(this.initialization); assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedFactoryMethod.class.getPackageName())).isTrue(); GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName()); @@ -189,7 +196,7 @@ class BeanRegistrationBeanFactoryContributionTests { beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class); // This resolve the generic parameter to a protected type beanDefinition.setTargetType(PublicFactoryBean.resolveToProtectedGenericParameter()); - getContribution(beanDefinition, singleConstructor(PublicFactoryBean.class)).applyTo(this.initialization); + getContributionFor(beanDefinition, singleConstructor(PublicFactoryBean.class)).applyTo(this.initialization); assertThat(this.generatedTypeContext.hasGeneratedType(PublicFactoryBean.class.getPackageName())).isTrue(); GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(PublicFactoryBean.class.getPackageName()); assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence(""" @@ -203,106 +210,96 @@ class BeanRegistrationBeanFactoryContributionTests { @Test void generateWithBeanDefinitionHavingSyntheticFlag() { - assertThat(simpleConfigurationRegistration(bd -> bd.setSynthetic(true)).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setSynthetic(true)).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setSynthetic(true)), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.isSynthetic()).isTrue())); } @Test void generateWithBeanDefinitionHavingDependsOn() { - assertThat(simpleConfigurationRegistration(bd -> bd.setDependsOn("test")).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setDependsOn(new String[] { "test" })).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setDependsOn("test")), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDependsOn()).containsExactly("test"))); } @Test void generateWithBeanDefinitionHavingLazyInit() { - assertThat(simpleConfigurationRegistration(bd -> bd.setLazyInit(true)).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setLazyInit(true)).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setLazyInit(true)), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.isLazyInit()).isTrue())); } @Test void generateWithBeanDefinitionHavingRole() { - assertThat(simpleConfigurationRegistration(bd -> bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setRole(2)).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.getRole()) + .isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE))); } @Test void generateWithBeanDefinitionHavingScope() { - assertThat(simpleConfigurationRegistration(bd -> bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setScope("prototype")).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.getScope()) + .isEqualTo(ConfigurableBeanFactory.SCOPE_PROTOTYPE))); } @Test void generateWithBeanDefinitionHavingAutowiredCandidate() { - assertThat(simpleConfigurationRegistration(bd -> bd.setAutowireCandidate(false)).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.setAutowireCandidate(false)).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.setAutowireCandidate(false)), + hasBeanDefinition(generatedBd -> assertThat(generatedBd.isAutowireCandidate()).isFalse())); } @Test - void generateWithBeanDefinitionHavingDefaultAutowiredCandidateDoesNotConfigureIt() { - assertThat(simpleConfigurationRegistration(bd -> bd.setAutowireCandidate(true)).getSnippet()) - .doesNotContain("bd.setAutowireCandidate("); + void generateWithBeanDefinitionHavingDefaultKeepsThem() { + compile(simpleConfigurationRegistration(bd -> {}), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.isSynthetic()).isFalse(); + assertThat(generatedBd.getDependsOn()).isNull(); + assertThat(generatedBd.isLazyInit()).isFalse(); + assertThat(generatedBd.getRole()).isEqualTo(BeanDefinition.ROLE_APPLICATION); + assertThat(generatedBd.getScope()).isEqualTo(ConfigurableBeanFactory.SCOPE_SINGLETON); + assertThat(generatedBd.isAutowireCandidate()).isTrue(); + })); } @Test void generateWithBeanDefinitionHavingMultipleAttributes() { - assertThat(simpleConfigurationRegistration(bd -> { + compile(simpleConfigurationRegistration(bd -> { bd.setSynthetic(true); bd.setPrimary(true); - }).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> { - bd.setPrimary(true); - bd.setSynthetic(true); - }).register(beanFactory); - """); + }), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.isSynthetic()).isTrue(); + assertThat(generatedBd.isPrimary()).isTrue(); + })); } @Test void generateWithBeanDefinitionHavingProperty() { - assertThat(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue("test", "Hello")).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("test", "Hello")).register(beanFactory); - """); + compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue("test", "Hello")), + hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getPropertyValues().contains("test")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello"); + })); } @Test void generateWithBeanDefinitionHavingSeveralProperties() { - CodeSnippet registration = simpleConfigurationRegistration(bd -> { + compile(simpleConfigurationRegistration(bd -> { bd.getPropertyValues().addPropertyValue("test", "Hello"); bd.getPropertyValues().addPropertyValue("counter", 42); - }); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> { - MutablePropertyValues propertyValues = bd.getPropertyValues(); - propertyValues.addPropertyValue("test", "Hello"); - propertyValues.addPropertyValue("counter", 42); - }).register(beanFactory); - """); - assertThat(registration.hasImport(MutablePropertyValues.class)).isTrue(); + }), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getPropertyValues().contains("test")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello"); + assertThat(generatedBd.getPropertyValues().contains("counter")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("counter")).isEqualTo(42); + })); } @Test void generateWithBeanDefinitionHavingPropertyReference() { - CodeSnippet registration = simpleConfigurationRegistration(bd -> bd.getPropertyValues() - .addPropertyValue("myService", new RuntimeBeanReference("test"))); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> SimpleConfiguration::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("myService", new RuntimeBeanReference("test"))).register(beanFactory); - """); - assertThat(registration.hasImport(RuntimeBeanReference.class)).isTrue(); + compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue( + "myService", new RuntimeBeanReference("test"))), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getPropertyValues().contains("myService")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("myService")) + .isInstanceOfSatisfying(RuntimeBeanReference.class, ref -> + assertThat(ref.getBeanName()).isEqualTo("test")); + })); } @Test @@ -312,14 +309,11 @@ class BeanRegistrationBeanFactoryContributionTests { .getBeanDefinition(); BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) .addPropertyValue("name", innerBeanDefinition).getBeanDefinition(); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); - CodeSnippet registration = CodeSnippet.of(this.initialization.toCodeBlock()); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", ConfigurableBean.class) - .instanceSupplier(ConfigurableBean::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("name", BeanDefinitionRegistrar.inner(SimpleConfiguration.class).withFactoryMethod(SimpleConfiguration.class, "stringBean") - .instanceSupplier(() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()).toBeanDefinition())).register(beanFactory); - """); - assertThat(registration.hasImport(SimpleConfiguration.class)).isTrue(); + compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getPropertyValues().contains("name")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("name")).isInstanceOfSatisfying(RootBeanDefinition.class, innerGeneratedBd -> + assertThat(innerGeneratedBd.getResolvedFactoryMethod()).isEqualTo(method(SimpleConfiguration.class, "stringBean"))); + })); } @Test @@ -329,15 +323,10 @@ class BeanRegistrationBeanFactoryContributionTests { .getBeanDefinition(); BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) .addPropertyValue("names", List.of(innerBeanDefinition, innerBeanDefinition)).getBeanDefinition(); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); - CodeSnippet registration = CodeSnippet.of(this.initialization.toCodeBlock()); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", ConfigurableBean.class) - .instanceSupplier(ConfigurableBean::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("names", List.of(BeanDefinitionRegistrar.inner(SimpleConfiguration.class).withFactoryMethod(SimpleConfiguration.class, "stringBean") - .instanceSupplier(() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()).toBeanDefinition(), BeanDefinitionRegistrar.inner(SimpleConfiguration.class).withFactoryMethod(SimpleConfiguration.class, "stringBean") - .instanceSupplier(() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()).toBeanDefinition()))).register(beanFactory); - """); - assertThat(registration.hasImport(SimpleConfiguration.class)).isTrue(); + compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getPropertyValues().contains("names")).isTrue(); + assertThat(generatedBd.getPropertyValues().get("names")).asList().hasSize(2); + })); } @Test @@ -347,7 +336,7 @@ class BeanRegistrationBeanFactoryContributionTests { .setRole(2).getBeanDefinition(); BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) .addPropertyValue("name", innerBeanDefinition).getBeanDefinition(); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); + getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); CodeSnippet registration = CodeSnippet.of(this.initialization.toCodeBlock()); assertThat(registration.getSnippet()).isEqualTo(""" BeanDefinitionRegistrar.of("test", ConfigurableBean.class) @@ -361,37 +350,53 @@ class BeanRegistrationBeanFactoryContributionTests { void generateUsingSingleConstructorArgument() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition(); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "hello"); - CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "create", String.class), - code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(SampleFactory.class, "create", String.class) - .instanceSupplier(() -> test).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, "hello")).register(beanFactory); - """); + compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", String.class)), beanFactory -> + assertThat(beanFactory.getBean(String.class)).isEqualTo("hello")); } @Test void generateUsingSeveralConstructorArguments() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class) - .addConstructorArgValue(42).addConstructorArgReference("testBean") + .addConstructorArgValue(42).addConstructorArgValue("testBean") .getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "create", Number.class, String.class), - code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(SampleFactory.class, "create", Number.class, String.class) - .instanceSupplier(() -> test).customize((bd) -> { - ConstructorArgumentValues argumentValues = bd.getConstructorArgumentValues(); - argumentValues.addIndexedArgumentValue(0, 42); - argumentValues.addIndexedArgumentValue(1, new RuntimeBeanReference("testBean")); - }).register(beanFactory); - """); - assertThat(registration.hasImport(ConstructorArgumentValues.class)).isTrue(); + compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", Number.class, String.class)), beanFactory -> + assertThat(beanFactory.getBean(String.class)).isEqualTo("42testBean")); + } + + @Test + void generateWithBeanDefinitionHavingAttributesDoesNotWriteThemByDefault() { + compile(simpleConfigurationRegistration(bd -> { + bd.setAttribute("test", "value"); + bd.setAttribute("counter", 42); + }), hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getAttribute("test")).isNull(); + assertThat(generatedBd.getAttribute("counter")).isNull(); + })); + } + + @Test + void generateWithBeanDefinitionHavingAttributesUseCustomFilter() { + RootBeanDefinition bd = new RootBeanDefinition(SimpleConfiguration.class); + bd.setAttribute("test", "value"); + bd.setAttribute("counter", 42); + DefaultBeanInstantiationGenerator beanInstantiationGenerator = new DefaultBeanInstantiationGenerator( + singleConstructor(SimpleConfiguration.class), Collections.emptyList()); + compile(new BeanRegistrationBeanFactoryContribution("test", bd, beanInstantiationGenerator) { + @Override + protected Predicate getAttributeFilter() { + return candidate -> candidate.equals("counter"); + } + }, hasBeanDefinition(generatedBd -> { + assertThat(generatedBd.getAttribute("test")).isNull(); + assertThat(generatedBd.getAttribute("counter")).isNotNull().isEqualTo(42); + })); } @Test void registerRuntimeHintsWithNoPropertyValuesDoesNotAccessRuntimeHints() { RootBeanDefinition bd = new RootBeanDefinition(String.class); RuntimeHints runtimeHints = mock(RuntimeHints.class); - getContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); + getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); verifyNoInteractions(runtimeHints); } @@ -401,7 +406,7 @@ class BeanRegistrationBeanFactoryContributionTests { .addPropertyValue("notAProperty", "invalid").addPropertyValue("name", "hello") .getBeanDefinition(); RuntimeHints runtimeHints = new RuntimeHints(); - getContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); + getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); assertThat(runtimeHints.reflection().getTypeHint(ConfigurableBean.class)).satisfies(hint -> { assertThat(hint.fields()).isEmpty(); assertThat(hint.constructors()).isEmpty(); @@ -421,7 +426,7 @@ class BeanRegistrationBeanFactoryContributionTests { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class) .addConstructorArgReference("environment") .addPropertyValue("name", "Hello").getBeanDefinition(); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); + getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class)); @@ -443,7 +448,7 @@ class BeanRegistrationBeanFactoryContributionTests { void registerRuntimeHintsForProperties() { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class) .addPropertyValue("name", "Hello").addPropertyValue("counter", 42).getBeanDefinition(); - getContribution(new DefaultListableBeanFactory(), beanDefinition).applyTo(this.initialization); + getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition).applyTo(this.initialization); ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); assertThat(reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); @@ -463,7 +468,7 @@ class BeanRegistrationBeanFactoryContributionTests { .addPropertyValue("counter", innerBd).getBeanDefinition(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("environment", Environment.class); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); + getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); @@ -489,7 +494,7 @@ class BeanRegistrationBeanFactoryContributionTests { .addPropertyValue("counters", List.of(innerBd1, innerBd2)).getBeanDefinition(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerSingleton("environment", Environment.class); - getContribution(beanFactory, beanDefinition).applyTo(this.initialization); + getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); @@ -520,23 +525,29 @@ class BeanRegistrationBeanFactoryContributionTests { return methodHint("", parameterTypes); } + private Consumer hasBeanDefinition(Consumer bd) { + return beanFactory -> { + assertThat(beanFactory.getBeanDefinitionNames()).contains("test"); + RootBeanDefinition beanDefinition = (RootBeanDefinition) beanFactory.getMergedBeanDefinition("test"); + bd.accept(beanDefinition); + }; + } - private CodeSnippet simpleConfigurationRegistration(Consumer bd) { + private BeanFactoryContribution simpleConfigurationRegistration(Consumer bd) { RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder .rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition(); bd.accept(beanDefinition); - return beanRegistration(beanDefinition, singleConstructor(SimpleConfiguration.class), - code -> code.add("() -> SimpleConfiguration::new")); + return getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition); } - private BeanRegistrationBeanFactoryContribution getContribution(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) { + private BeanRegistrationBeanFactoryContribution getDefaultContribution(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) { BeanRegistrationBeanFactoryContribution contribution = new DefaultBeanRegistrationContributionProvider(beanFactory) .getContributionFor("test", (RootBeanDefinition) beanDefinition); assertThat(contribution).isNotNull(); return contribution; } - private BeanFactoryContribution getContribution(BeanDefinition beanDefinition, Executable instanceCreator) { + private BeanRegistrationBeanFactoryContribution getContributionFor(BeanDefinition beanDefinition, Executable instanceCreator) { return new BeanRegistrationBeanFactoryContribution("test", (RootBeanDefinition) beanDefinition, new DefaultBeanInstantiationGenerator(instanceCreator, Collections.emptyList())); } @@ -589,6 +600,30 @@ class BeanRegistrationBeanFactoryContributionTests { }).collect(Collectors.joining("\n")); } + private void compile(BeanFactoryContribution contribution, Consumer beanFactory) { + contribution.applyTo(this.initialization); + GeneratedType generatedType = this.generatedTypeContext.getMainGeneratedType(); + generatedType.customizeType(type -> { + type.addModifiers(Modifier.PUBLIC); + type.addSuperinterface(BeanFactoryInitializer.class); + }); + generatedType.addMethod(MethodSpec.methodBuilder("initializeBeanFactory") + .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class) + .addParameter(DefaultListableBeanFactory.class, "beanFactory") + .addCode(this.initialization.toCodeBlock())); + SourceFiles sourceFiles = SourceFiles.none(); + for (JavaFile javaFile : this.generatedTypeContext.toJavaFiles()) { + sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo))); + } + TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> { + BeanFactoryInitializer initializer = compiled.getInstance(BeanFactoryInitializer.class, + generatedType.getClassName().canonicalName()); + DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(); + initializer.initializeBeanFactory(freshBeanFactory); + beanFactory.accept(freshBeanFactory); + }); + } + static abstract class BaseFactoryBean { public void setName(String name) { diff --git a/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/generator/BeanFactoryInitializer.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/generator/BeanFactoryInitializer.java new file mode 100644 index 00000000000..54854841011 --- /dev/null +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/generator/BeanFactoryInitializer.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.testfixture.beans.factory.generator; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; + +@FunctionalInterface +public interface BeanFactoryInitializer { + + void initializeBeanFactory(DefaultListableBeanFactory beanFactory); + +}