diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java index 592d1c665d..577631ff54 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java @@ -33,7 +33,6 @@ import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.MethodReference; import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator; import org.springframework.aot.test.generate.TestGenerationContext; -import org.springframework.aot.test.generate.TestTarget; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -97,7 +96,7 @@ class BeanRegistrationsAotContributionTests { @Test void applyToWhenHasNameGeneratesPrefixedFeatureName() { this.generationContext = new TestGenerationContext( - new ClassNameGenerator(TestTarget.class, "Management")); + new ClassNameGenerator(TestGenerationContext.TEST_TARGET, "Management")); this.beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(this.generationContext); Map registrations = new LinkedHashMap<>(); RegisteredBean registeredBean = registerBean( diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestGenerationContext.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestGenerationContext.java index fa95ea3082..77b19e10a1 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestGenerationContext.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestGenerationContext.java @@ -23,6 +23,7 @@ import org.springframework.aot.generate.DefaultGenerationContext; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.InMemoryGeneratedFiles; import org.springframework.core.test.tools.TestCompiler; +import org.springframework.javapoet.ClassName; /** * {@link GenerationContext} test implementation that uses @@ -35,6 +36,11 @@ import org.springframework.core.test.tools.TestCompiler; */ public class TestGenerationContext extends DefaultGenerationContext implements UnaryOperator { + /** + * The default test target {@link ClassName}. + */ + public static final ClassName TEST_TARGET = ClassName.get("com.example", "TestTarget"); + /** * Create an instance using the specified {@link ClassNameGenerator}. * @param classNameGenerator the class name generator to use @@ -47,15 +53,15 @@ public class TestGenerationContext extends DefaultGenerationContext implements U * Create an instance using the specified {@code target}. * @param target the default target class to use */ - public TestGenerationContext(Class target) { + public TestGenerationContext(ClassName target) { this(new ClassNameGenerator(target)); } /** - * Create an instance using {@link TestTarget} as the {@code target}. + * Create an instance using {@link #TEST_TARGET} as the {@code target}. */ public TestGenerationContext() { - this(TestTarget.class); + this(TEST_TARGET); } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestTarget.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestTarget.java deleted file mode 100644 index f63ead634e..0000000000 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/TestTarget.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.aot.test.generate; - -/** - * A default target class used by tests of code generation. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class TestTarget { -} diff --git a/spring-core/src/main/java/org/springframework/aot/generate/ClassNameGenerator.java b/spring-core/src/main/java/org/springframework/aot/generate/ClassNameGenerator.java index 80ea3dc60d..48c88041e4 100644 --- a/spring-core/src/main/java/org/springframework/aot/generate/ClassNameGenerator.java +++ b/spring-core/src/main/java/org/springframework/aot/generate/ClassNameGenerator.java @@ -27,8 +27,8 @@ import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** - * Generate unique class names based on a target {@link Class} and a feature - * name. + * Generate unique class names based on a target {@link ClassName} and a + * feature name. * *

This class is stateful, so the same instance should be used for all name * generation. @@ -43,7 +43,7 @@ public final class ClassNameGenerator { private static final String AOT_FEATURE = "Aot"; - private final Class defaultTarget; + private final ClassName defaultTarget; private final String featureNamePrefix; @@ -55,7 +55,7 @@ public final class ClassNameGenerator { * feature name prefix. * @param defaultTarget the default target class to use */ - public ClassNameGenerator(Class defaultTarget) { + public ClassNameGenerator(ClassName defaultTarget) { this(defaultTarget, ""); } @@ -65,11 +65,11 @@ public final class ClassNameGenerator { * @param defaultTarget the default target class to use * @param featureNamePrefix the prefix to use to qualify feature names */ - public ClassNameGenerator(Class defaultTarget, String featureNamePrefix) { + public ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix) { this(defaultTarget, featureNamePrefix, new ConcurrentHashMap<>()); } - private ClassNameGenerator(Class defaultTarget, String featureNamePrefix, + private ClassNameGenerator(ClassName defaultTarget, String featureNamePrefix, Map sequenceGenerator) { Assert.notNull(defaultTarget, "'defaultTarget' must not be null"); this.defaultTarget = defaultTarget; @@ -98,16 +98,16 @@ public final class ClassNameGenerator { * {@code null} to use the main target * @return a unique generated class name */ - public ClassName generateClassName(String featureName, @Nullable Class target) { + public ClassName generateClassName(String featureName, @Nullable ClassName target) { return generateSequencedClassName(getRootName(featureName, target)); } - private String getRootName(String featureName, @Nullable Class target) { + private String getRootName(String featureName, @Nullable ClassName target) { Assert.hasLength(featureName, "'featureName' must not be empty"); featureName = clean(featureName); - Class targetToUse = (target != null ? target : this.defaultTarget); + ClassName targetToUse = (target != null ? target : this.defaultTarget); String featureNameToUse = this.featureNamePrefix + featureName; - return targetToUse.getName().replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse); + return toName(targetToUse).replace("$", "_") + SEPARATOR + StringUtils.capitalize(featureNameToUse); } private String clean(String name) { @@ -147,4 +147,8 @@ public final class ClassNameGenerator { this.sequenceGenerator); } + private static String toName(ClassName className) { + return GeneratedTypeReference.of(className).getName(); + } + } diff --git a/spring-core/src/main/java/org/springframework/aot/generate/GeneratedClasses.java b/spring-core/src/main/java/org/springframework/aot/generate/GeneratedClasses.java index 27be83e168..aa768ff406 100644 --- a/spring-core/src/main/java/org/springframework/aot/generate/GeneratedClasses.java +++ b/spring-core/src/main/java/org/springframework/aot/generate/GeneratedClasses.java @@ -99,7 +99,7 @@ public class GeneratedClasses { * @return an existing or newly generated class */ public GeneratedClass getOrAddForFeatureComponent(String featureName, - Class targetComponent, Consumer type) { + ClassName targetComponent, Consumer type) { Assert.hasLength(featureName, "'featureName' must not be empty"); Assert.notNull(targetComponent, "'targetComponent' must not be null"); @@ -111,6 +111,24 @@ public class GeneratedClasses { return generatedClass; } + /** + * Get or add a generated class for the specified {@code featureName} + * targeting the specified {@code component}. If this method has previously + * been called with the given {@code featureName}/{@code target} the + * existing class will be returned, otherwise a new class will be generated, + * otherwise a new class will be generated. + * @param featureName the name of the feature to associate with the + * generated class + * @param targetComponent the target component + * @param type a {@link Consumer} used to build the type + * @return an existing or newly generated class + */ + public GeneratedClass getOrAddForFeatureComponent(String featureName, + Class targetComponent, Consumer type) { + + return getOrAddForFeatureComponent(featureName, ClassName.get(targetComponent), type); + } + /** * Add a new generated class for the specified {@code featureName} and no * particular component. @@ -135,7 +153,7 @@ public class GeneratedClasses { * @return the newly generated class */ public GeneratedClass addForFeatureComponent(String featureName, - Class targetComponent, Consumer type) { + ClassName targetComponent, Consumer type) { Assert.hasLength(featureName, "'featureName' must not be empty"); Assert.notNull(targetComponent, "'targetComponent' must not be null"); @@ -143,8 +161,23 @@ public class GeneratedClasses { return createAndAddGeneratedClass(featureName, targetComponent, type); } + /** + * Add a new generated class for the specified {@code featureName} targeting + * the specified {@code component}. + * @param featureName the name of the feature to associate with the + * generated class + * @param targetComponent the target component + * @param type a {@link Consumer} used to build the type + * @return the newly generated class + */ + public GeneratedClass addForFeatureComponent(String featureName, + Class targetComponent, Consumer type) { + + return addForFeatureComponent(featureName, ClassName.get(targetComponent), type); + } + private GeneratedClass createAndAddGeneratedClass(String featureName, - @Nullable Class targetComponent, Consumer type) { + @Nullable ClassName targetComponent, Consumer type) { ClassName className = this.classNameGenerator.generateClassName(featureName, targetComponent); GeneratedClass generatedClass = new GeneratedClass(className, type); @@ -171,7 +204,7 @@ public class GeneratedClasses { this.classes, this.classesByOwner); } - private record Owner(String featureNamePrefix, String featureName, @Nullable Class target) { + private record Owner(String featureNamePrefix, String featureName, @Nullable ClassName target) { } } diff --git a/spring-core/src/test/java/org/springframework/aot/generate/ClassNameGeneratorTests.java b/spring-core/src/test/java/org/springframework/aot/generate/ClassNameGeneratorTests.java index de6f780178..b1867a0583 100644 --- a/spring-core/src/test/java/org/springframework/aot/generate/ClassNameGeneratorTests.java +++ b/spring-core/src/test/java/org/springframework/aot/generate/ClassNameGeneratorTests.java @@ -32,71 +32,69 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException */ class ClassNameGeneratorTests { - private final ClassNameGenerator generator = new ClassNameGenerator(Object.class); + private static final ClassName TEST_TARGET = ClassName.get("com.example", "Test"); + + private final ClassNameGenerator generator = new ClassNameGenerator(TEST_TARGET); @Test void generateClassNameWhenTargetClassIsNullUsesMainTarget() { ClassName generated = this.generator.generateClassName("test", null); - assertThat(generated).hasToString("java.lang.Object__Test"); + assertThat(generated).hasToString("com.example.Test__Test"); } @Test void generateClassNameUseFeatureNamePrefix() { - ClassName generated = new ClassNameGenerator(Object.class, "One") - .generateClassName("test", InputStream.class); + ClassName generated = new ClassNameGenerator(TEST_TARGET, "One") + .generateClassName("test", ClassName.get(InputStream.class)); assertThat(generated).hasToString("java.io.InputStream__OneTest"); } @Test void generateClassNameWithNoTextFeatureNamePrefix() { - ClassName generated = new ClassNameGenerator(Object.class, " ") - .generateClassName("test", InputStream.class); + ClassName generated = new ClassNameGenerator(TEST_TARGET, " ") + .generateClassName("test", ClassName.get(InputStream.class)); assertThat(generated).hasToString("java.io.InputStream__Test"); } @Test void generatedClassNameWhenFeatureIsEmptyThrowsException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.generator.generateClassName("", InputStream.class)) + .isThrownBy(() -> this.generator.generateClassName("", ClassName.get(InputStream.class))) .withMessage("'featureName' must not be empty"); } @Test void generatedClassNameWhenFeatureIsNotAllLettersThrowsException() { - assertThat(this.generator.generateClassName("name!", InputStream.class)) + assertThat(this.generator.generateClassName("name!", ClassName.get(InputStream.class))) .hasToString("java.io.InputStream__Name"); - assertThat(this.generator.generateClassName("1NameHere", InputStream.class)) + assertThat(this.generator.generateClassName("1NameHere", ClassName.get(InputStream.class))) .hasToString("java.io.InputStream__NameHere"); - assertThat(this.generator.generateClassName("Y0pe", InputStream.class)) + assertThat(this.generator.generateClassName("Y0pe", ClassName.get(InputStream.class))) .hasToString("java.io.InputStream__YPe"); } @Test void generateClassNameWithClassWhenLowercaseFeatureNameGeneratesName() { - ClassName generated = this.generator.generateClassName("bytes", InputStream.class); + ClassName generated = this.generator.generateClassName("bytes", ClassName.get(InputStream.class)); assertThat(generated).hasToString("java.io.InputStream__Bytes"); } @Test void generateClassNameWithClassWhenInnerClassGeneratesName() { - ClassName generated = this.generator.generateClassName("EventListener", TestBean.class); + ClassName innerBean = ClassName.get("com.example", "Test", "InnerBean"); + ClassName generated = this.generator.generateClassName("EventListener", innerBean); assertThat(generated) - .hasToString("org.springframework.aot.generate.ClassNameGeneratorTests_TestBean__EventListener"); + .hasToString("com.example.Test_InnerBean__EventListener"); } @Test void generateClassWithClassWhenMultipleCallsGeneratesSequencedName() { - ClassName generated1 = this.generator.generateClassName("bytes", InputStream.class); - ClassName generated2 = this.generator.generateClassName("bytes", InputStream.class); - ClassName generated3 = this.generator.generateClassName("bytes", InputStream.class); + ClassName generated1 = this.generator.generateClassName("bytes",ClassName.get(InputStream.class)); + ClassName generated2 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class)); + ClassName generated3 = this.generator.generateClassName("bytes", ClassName.get(InputStream.class)); assertThat(generated1).hasToString("java.io.InputStream__Bytes"); assertThat(generated2).hasToString("java.io.InputStream__Bytes1"); assertThat(generated3).hasToString("java.io.InputStream__Bytes2"); } - - static class TestBean { - - } - } diff --git a/spring-core/src/test/java/org/springframework/aot/generate/DefaultGenerationContextTests.java b/spring-core/src/test/java/org/springframework/aot/generate/DefaultGenerationContextTests.java index 137f166cb8..1db14ca7d3 100644 --- a/spring-core/src/test/java/org/springframework/aot/generate/DefaultGenerationContextTests.java +++ b/spring-core/src/test/java/org/springframework/aot/generate/DefaultGenerationContextTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.springframework.aot.generate.GeneratedFiles.Kind; import org.springframework.aot.hint.RuntimeHints; +import org.springframework.javapoet.ClassName; import org.springframework.javapoet.TypeSpec; import static org.assertj.core.api.Assertions.assertThat; @@ -35,10 +36,12 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException */ class DefaultGenerationContextTests { + private static final ClassName SAMPLE_TARGET = ClassName.get("com.example", "SampleTarget"); + private static final Consumer typeSpecCustomizer = type -> {}; private final GeneratedClasses generatedClasses = new GeneratedClasses( - new ClassNameGenerator(SampleTarget.class)); + new ClassNameGenerator(SAMPLE_TARGET)); private final InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles(); @@ -48,7 +51,7 @@ class DefaultGenerationContextTests { @Test void createWithOnlyGeneratedFilesCreatesContext() { DefaultGenerationContext context = new DefaultGenerationContext( - new ClassNameGenerator(SampleTarget.class), this.generatedFiles); + new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles); assertThat(context.getGeneratedFiles()).isSameAs(this.generatedFiles); assertThat(context.getRuntimeHints()).isInstanceOf(RuntimeHints.class); } @@ -109,7 +112,7 @@ class DefaultGenerationContextTests { @Test void withNameUpdateNamingConvention() { DefaultGenerationContext context = new DefaultGenerationContext( - new ClassNameGenerator(SampleTarget.class), this.generatedFiles); + new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles); GenerationContext anotherContext = context.withName("Another"); GeneratedClass generatedClass = anotherContext.getGeneratedClasses() .addForFeature("Test", typeSpecCustomizer); @@ -119,7 +122,7 @@ class DefaultGenerationContextTests { @Test void withNameKeepsTrackOfAllGeneratedFiles() { DefaultGenerationContext context = new DefaultGenerationContext( - new ClassNameGenerator(SampleTarget.class), this.generatedFiles); + new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles); context.getGeneratedClasses().addForFeature("Test", typeSpecCustomizer); GenerationContext anotherContext = context.withName("Another"); assertThat(anotherContext.getGeneratedClasses()).isNotSameAs(context.getGeneratedClasses()); @@ -133,7 +136,7 @@ class DefaultGenerationContextTests { @Test void withNameGeneratesUniqueName() { DefaultGenerationContext context = new DefaultGenerationContext( - new ClassNameGenerator(Object.class), this.generatedFiles); + new ClassNameGenerator(SAMPLE_TARGET), this.generatedFiles); context.withName("Test").getGeneratedClasses() .addForFeature("Feature", typeSpecCustomizer); context.withName("Test").getGeneratedClasses() @@ -142,11 +145,9 @@ class DefaultGenerationContextTests { .addForFeature("Feature", typeSpecCustomizer); context.writeGeneratedContent(); assertThat(this.generatedFiles.getGeneratedFiles(Kind.SOURCE)).containsOnlyKeys( - "java/lang/Object__TestFeature.java", - "java/lang/Object__Test1Feature.java", - "java/lang/Object__Test2Feature.java"); + "com/example/SampleTarget__TestFeature.java", + "com/example/SampleTarget__Test1Feature.java", + "com/example/SampleTarget__Test2Feature.java"); } - static class SampleTarget {} - } diff --git a/spring-core/src/test/java/org/springframework/aot/generate/GeneratedClassesTests.java b/spring-core/src/test/java/org/springframework/aot/generate/GeneratedClassesTests.java index 2edca07b55..df8a73c960 100644 --- a/spring-core/src/test/java/org/springframework/aot/generate/GeneratedClassesTests.java +++ b/spring-core/src/test/java/org/springframework/aot/generate/GeneratedClassesTests.java @@ -22,6 +22,7 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.aot.generate.GeneratedFiles.Kind; +import org.springframework.javapoet.ClassName; import org.springframework.javapoet.TypeSpec; import static org.assertj.core.api.Assertions.assertThat; @@ -42,7 +43,7 @@ class GeneratedClassesTests { private static final Consumer emptyTypeCustomizer = type -> {}; private final GeneratedClasses generatedClasses = new GeneratedClasses( - new ClassNameGenerator(Object.class)); + new ClassNameGenerator(ClassName.get("com.example", "Test"))); @Test void createWhenClassNameGeneratorIsNullThrowsException() { @@ -76,7 +77,7 @@ class GeneratedClassesTests { @Test void addForFeatureUsesDefaultTarget() { GeneratedClass generatedClass = this.generatedClasses.addForFeature("Test", emptyTypeCustomizer); - assertThat(generatedClass.getName()).hasToString("java.lang.Object__Test"); + assertThat(generatedClass.getName()).hasToString("com.example.Test__Test"); } @Test diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java index ae67e17a47..004f7a597a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java +++ b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java @@ -234,7 +234,7 @@ public class TestContextAotGenerator { } DefaultGenerationContext createGenerationContext(Class testClass) { - ClassNameGenerator classNameGenerator = new ClassNameGenerator(testClass); + ClassNameGenerator classNameGenerator = new ClassNameGenerator(ClassName.get(testClass)); DefaultGenerationContext generationContext = new DefaultGenerationContext(classNameGenerator, this.generatedFiles, this.runtimeHints); return generationContext.withName(nextTestContextId()); @@ -245,7 +245,8 @@ public class TestContextAotGenerator { } private void generateTestAotMappings(MultiValueMap> initializerClassMappings) { - ClassNameGenerator classNameGenerator = new ClassNameGenerator(AotTestContextInitializers.class); + ClassNameGenerator classNameGenerator = new ClassNameGenerator( + ClassName.get(AotTestContextInitializers.class)); DefaultGenerationContext generationContext = new DefaultGenerationContext(classNameGenerator, this.generatedFiles, this.runtimeHints); GeneratedClasses generatedClasses = generationContext.getGeneratedClasses(); @@ -258,7 +259,8 @@ public class TestContextAotGenerator { } private void generateAotTestAttributes() { - ClassNameGenerator classNameGenerator = new ClassNameGenerator(AotTestAttributes.class); + ClassNameGenerator classNameGenerator = new ClassNameGenerator( + ClassName.get(AotTestAttributes.class)); DefaultGenerationContext generationContext = new DefaultGenerationContext(classNameGenerator, this.generatedFiles, this.runtimeHints); GeneratedClasses generatedClasses = generationContext.getGeneratedClasses();