Generate matching structure for configuration classes
This commit improves GeneratedClass to support inner classes, allowing them to be registered by name with a type customizer, as GeneratedClasses does for top level classes. BeanDefinitionMethodGenerator leverages this feature to create a matching structure for configuration classes that contain inner classes. Closes gh-29213
This commit is contained in:
@@ -44,6 +44,7 @@ import org.springframework.util.StringUtils;
|
||||
* Generates a method that returns a {@link BeanDefinition} to be registered.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see BeanDefinitionMethodGeneratorFactory
|
||||
*/
|
||||
@@ -97,11 +98,7 @@ class BeanDefinitionMethodGenerator {
|
||||
ClassName target = codeFragments.getTarget(this.registeredBean,
|
||||
this.constructorOrFactoryMethod);
|
||||
if (!target.canonicalName().startsWith("java.")) {
|
||||
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
|
||||
.getOrAddForFeatureComponent("BeanDefinitions", target, type -> {
|
||||
type.addJavadoc("Bean definitions for {@link $T}", target);
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
});
|
||||
GeneratedClass generatedClass = lookupGeneratedClass(generationContext, target);
|
||||
GeneratedMethods generatedMethods = generatedClass.getMethods()
|
||||
.withPrefix(getName());
|
||||
GeneratedMethod generatedMethod = generateBeanDefinitionMethod(
|
||||
@@ -117,6 +114,43 @@ class BeanDefinitionMethodGenerator {
|
||||
return generatedMethod.toMethodReference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link GeneratedClass} to use for the specified {@code target}.
|
||||
* <p>If the target class is an inner class, a corresponding inner class in
|
||||
* the original structure is created.
|
||||
* @param generationContext the generation context to use
|
||||
* @param target the chosen target class name for the bean definition
|
||||
* @return the generated class to use
|
||||
*/
|
||||
private static GeneratedClass lookupGeneratedClass(GenerationContext generationContext, ClassName target) {
|
||||
ClassName topLevelClassName = target.topLevelClassName();
|
||||
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
|
||||
.getOrAddForFeatureComponent("BeanDefinitions", topLevelClassName, type -> {
|
||||
type.addJavadoc("Bean definitions for {@link $T}", topLevelClassName);
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
});
|
||||
List<String> names = target.simpleNames();
|
||||
if (names.size() == 1) {
|
||||
return generatedClass;
|
||||
}
|
||||
List<String> namesToProcess = names.subList(1, names.size());
|
||||
ClassName currentTargetClassName = topLevelClassName;
|
||||
GeneratedClass tmp = generatedClass;
|
||||
for (String nameToProcess : namesToProcess) {
|
||||
currentTargetClassName = currentTargetClassName.nestedClass(nameToProcess);
|
||||
tmp = createInnerClass(tmp, nameToProcess + "__BeanDefinitions", currentTargetClassName);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private static GeneratedClass createInnerClass(GeneratedClass generatedClass,
|
||||
String name, ClassName target) {
|
||||
return generatedClass.getOrAdd(name, type -> {
|
||||
type.addJavadoc("Bean definitions for {@link $T}", target);
|
||||
type.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||
});
|
||||
}
|
||||
|
||||
private BeanRegistrationCodeFragments getCodeFragments(GenerationContext generationContext,
|
||||
BeanRegistrationsCode beanRegistrationsCode) {
|
||||
|
||||
|
||||
@@ -42,7 +42,9 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.AnnotatedBean;
|
||||
import org.springframework.beans.testfixture.beans.GenericBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.InnerBeanConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
@@ -60,6 +62,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link DefaultBeanRegistrationCodeFragments}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
@@ -99,6 +102,52 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasInnerClassTargetMethodGeneratesMethod() {
|
||||
this.beanFactory.registerBeanDefinition("testBeanConfiguration", new RootBeanDefinition(
|
||||
InnerBeanConfiguration.Simple.class));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("testBeanConfiguration");
|
||||
beanDefinition.setFactoryMethodName("simpleBean");
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile.getClassName()).endsWith("InnerBeanConfiguration__BeanDefinitions");
|
||||
assertThat(sourceFile).contains("public static class Simple__BeanDefinitions")
|
||||
.contains("Bean definitions for {@link InnerBeanConfiguration.Simple}")
|
||||
.doesNotContain("Another__BeanDefinitions");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasNestedInnerClassTargetMethodGeneratesMethod() {
|
||||
this.beanFactory.registerBeanDefinition("testBeanConfiguration", new RootBeanDefinition(
|
||||
InnerBeanConfiguration.Simple.Another.class));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("testBeanConfiguration");
|
||||
beanDefinition.setFactoryMethodName("anotherBean");
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile.getClassName()).endsWith("InnerBeanConfiguration__BeanDefinitions");
|
||||
assertThat(sourceFile).contains("public static class Simple__BeanDefinitions")
|
||||
.contains("Bean definitions for {@link InnerBeanConfiguration.Simple}")
|
||||
.contains("public static class Another__BeanDefinitions")
|
||||
.contains("Bean definitions for {@link InnerBeanConfiguration.Simple.Another}");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasGenericsGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
/**
|
||||
* A configuration with inner classes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class InnerBeanConfiguration {
|
||||
|
||||
public static class Simple {
|
||||
|
||||
public SimpleBean simpleBean() {
|
||||
return new SimpleBean();
|
||||
}
|
||||
|
||||
public static class Another {
|
||||
|
||||
public SimpleBean anotherBean() {
|
||||
return new SimpleBean();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.beans.testfixture.beans.factory.aot;
|
||||
|
||||
/**
|
||||
* A sample configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user