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 b01dfce999..3629af2d49 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 @@ -324,8 +324,8 @@ public class TestContextAotGenerator { DefaultGenerationContext createGenerationContext(Class> testClass) { ClassNameGenerator classNameGenerator = new ClassNameGenerator(ClassName.get(testClass)); - DefaultGenerationContext generationContext = - new DefaultGenerationContext(classNameGenerator, this.generatedFiles, this.runtimeHints); + TestContextGenerationContext generationContext = + new TestContextGenerationContext(classNameGenerator, this.generatedFiles, this.runtimeHints); return generationContext.withName(nextTestContextId()); } diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextGenerationContext.java b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextGenerationContext.java new file mode 100644 index 0000000000..2220386293 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextGenerationContext.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2023 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.test.context.aot; + +import org.springframework.aot.generate.ClassNameGenerator; +import org.springframework.aot.generate.DefaultGenerationContext; +import org.springframework.aot.generate.GeneratedClasses; +import org.springframework.aot.generate.GeneratedFiles; +import org.springframework.aot.hint.RuntimeHints; + +/** + * Extension of {@link DefaultGenerationContext} with a custom implementation of + * {@link #withName(String)} that is specific to the Spring TestContext Framework. + * + * @author Sam Brannen + * @since 6.0.12 + */ +class TestContextGenerationContext extends DefaultGenerationContext { + + private final String featureName; + + + /** + * Create a new {@link TestContextGenerationContext} instance backed by the + * specified {@link ClassNameGenerator}, {@link GeneratedFiles}, and + * {@link RuntimeHints}. + * @param classNameGenerator the naming convention to use for generated class names + * @param generatedFiles the generated files + * @param runtimeHints the runtime hints + */ + TestContextGenerationContext(ClassNameGenerator classNameGenerator, GeneratedFiles generatedFiles, + RuntimeHints runtimeHints) { + super(classNameGenerator, generatedFiles, runtimeHints); + this.featureName = null; + } + + /** + * Create a new {@link TestContextGenerationContext} instance backed by the + * specified {@link GeneratedClasses}, {@link GeneratedFiles}, and + * {@link RuntimeHints}. + * @param generatedClasses the generated classes + * @param generatedFiles the generated files + * @param runtimeHints the runtime hints + */ + private TestContextGenerationContext(GeneratedClasses generatedClasses, GeneratedFiles generatedFiles, + RuntimeHints runtimeHints, String featureName) { + super(generatedClasses, generatedFiles, runtimeHints); + this.featureName = featureName; + } + + + /** + * Create a new {@link TestContextGenerationContext} instance using the specified + * feature name to qualify generated assets for a dedicated round of code generation. + *
If this {@code TestContextGenerationContext} has a configured feature
+ * name, the supplied feature name will be appended to the existing feature name
+ * in order to avoid naming collisions.
+ * @param featureName the feature name to use
+ * @return a specialized {@link TestContextGenerationContext} for the specified
+ * feature name
+ */
+ @Override
+ public TestContextGenerationContext withName(String featureName) {
+ if (this.featureName != null) {
+ featureName = this.featureName + featureName;
+ }
+ GeneratedClasses generatedClasses = getGeneratedClasses().withFeatureNamePrefix(featureName);
+ return new TestContextGenerationContext(generatedClasses, getGeneratedFiles(), getRuntimeHints(), featureName);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/AbstractAotTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/AbstractAotTests.java
index 20c67d1dfc..679451263c 100644
--- a/spring-test/src/test/java/org/springframework/test/context/aot/AbstractAotTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/aot/AbstractAotTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 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.
@@ -45,14 +45,22 @@ abstract class AbstractAotTests {
"org/springframework/context/event/EventListenerMethodProcessor__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterSharedConfigTests__TestContext002_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterSharedConfigTests__TestContext002_BeanFactoryRegistrations.java",
+ "org/springframework/test/context/aot/samples/basic/BasicSpringJupiterSharedConfigTests__TestContext002_ManagementApplicationContextInitializer.java",
+ "org/springframework/test/context/aot/samples/basic/BasicSpringJupiterSharedConfigTests__TestContext002_ManagementBeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext002_BeanDefinitions.java",
+ "org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext002_BeanDefinitions.java",
+ "org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext002_ManagementBeanDefinitions.java",
// BasicSpringJupiterTests -- not generated b/c already generated for BasicSpringJupiterSharedConfigTests.
// BasicSpringJupiterTests.NestedTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext003_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests_NestedTests__TestContext003_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests_NestedTests__TestContext003_BeanFactoryRegistrations.java",
+ "org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests_NestedTests__TestContext003_ManagementApplicationContextInitializer.java",
+ "org/springframework/test/context/aot/samples/basic/BasicSpringJupiterTests_NestedTests__TestContext003_ManagementBeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext003_BeanDefinitions.java",
+ "org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext003_BeanDefinitions.java",
+ "org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext003_ManagementBeanDefinitions.java",
// BasicSpringTestNGTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext004_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext004_BeanDefinitions.java",
diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java
index 89701d6969..c7b749fc9a 100644
--- a/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/aot/AotIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 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.
@@ -36,6 +36,7 @@ import org.opentest4j.MultipleFailuresError;
import org.springframework.aot.AotDetector;
import org.springframework.aot.generate.GeneratedFiles.Kind;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
+import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.test.generate.CompilerFiles;
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
import org.springframework.core.test.tools.TestCompiler;
@@ -86,7 +87,7 @@ class AotIntegrationTests extends AbstractAotTests {
// AOT BUILD-TIME: PROCESSING
InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
- TestContextAotGenerator generator = new TestContextAotGenerator(generatedFiles);
+ TestContextAotGenerator generator = new TestContextAotGenerator(generatedFiles, new RuntimeHints(), true);
generator.processAheadOfTime(testClasses);
List See gh-30861.
+ *
+ * @author Sam Brannen
+ * @since 6.0.12
+ */
+@Configuration
+public class ManagementConfiguration {
+
+ @Bean
+ static BeanRegistrationAotProcessor beanRegistrationAotProcessor() {
+ return registeredBean -> {
+ Executable factoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
+ // Make AOT contribution for @Managed @Bean methods.
+ if (AnnotatedElementUtils.hasAnnotation(factoryMethod, Managed.class)) {
+ return new AotContribution(createManagementContext());
+ }
+ return null;
+ };
+ }
+
+ private static GenericApplicationContext createManagementContext() {
+ GenericApplicationContext managementContext = new GenericApplicationContext();
+ managementContext.registerBean(ManagementMessageService.class);
+ return managementContext;
+ }
+
+
+ /**
+ * Mimics Spring Boot's AOT support for child management contexts in
+ * {@code org.springframework.boot.actuate.autoconfigure.web.server.ChildManagementContextInitializer.AotContribution}.
+ */
+ private static class AotContribution implements BeanRegistrationAotContribution {
+
+ private final GenericApplicationContext managementContext;
+
+ AotContribution(GenericApplicationContext managementContext) {
+ this.managementContext = managementContext;
+ }
+
+ @Override
+ public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
+ GenerationContext managementGenerationContext = generationContext.withName("Management");
+ new ApplicationContextAotGenerator().processAheadOfTime(this.managementContext, managementGenerationContext);
+ }
+
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/management/ManagementMessageService.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/management/ManagementMessageService.java
new file mode 100644
index 0000000000..606eba7dde
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/management/ManagementMessageService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2023 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.test.context.aot.samples.management;
+
+import org.springframework.test.context.aot.samples.common.MessageService;
+
+/**
+ * {@link MessageService} implemented in a different package.
+ *
+ * See gh-30861.
+ *
+ * @author Sam Brannen
+ * @since 6.0.12
+ */
+class ManagementMessageService implements MessageService {
+
+ @Override
+ public String generateMessage() {
+ return "Hello, Management!";
+ }
+
+}