From 32346b838261215b984e38895e90e532eb63f050 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 16 Aug 2022 14:12:36 +0200 Subject: [PATCH 1/3] Introduce isSynthesizable in MergedAnnotation This commit adds the ability to check if a the annotation managed by a MergedAnnotation is synthesizable. This makes it easier to register a JDK proxy hint if necessary See gh-28967 --- .../core/annotation/MergedAnnotation.java | 9 +++++++ .../annotation/MissingMergedAnnotation.java | 7 ++++- .../core/annotation/TypeMappedAnnotation.java | 22 ++++++++------- .../annotation/MergedAnnotationsTests.java | 27 +++++++++++++++---- .../MissingMergedAnnotationTests.java | 5 ++++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java index 6d9c1bcf5de..8c909a8a5bb 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java @@ -477,6 +477,15 @@ public interface MergedAnnotation { */ > T asMap(Function, T> factory, Adapt... adaptations); + /** + * Determine if this merged annotation is synthesizable. + *

Consult the documentation for {@link #synthesize()} for an explanation + * of what is considered synthesizable. + * @return {@code true} if the mapped annotation is synthesizable + * @since 6.0 + */ + boolean isSynthesizable(); + /** * Create a type-safe synthesized version of this merged annotation that can * be used directly in code. diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java index 0c7c9abad61..75966992a44 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * 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. @@ -134,6 +134,11 @@ final class MissingMergedAnnotation extends AbstractMerged return factory.apply(this); } + @Override + public boolean isSynthesizable() { + return false; + } + @Override public String toString() { return "(missing)"; diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index 7457ddd6de5..1fcbb188832 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -319,6 +319,17 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return value; } + @Override + public boolean isSynthesizable() { + // Is this a mapped annotation for a composed annotation, and are there + // annotation attributes (mirrors) that need to be merged? + if (getDistance() > 0 && this.resolvedMirrors.length > 0) { + return true; + } + // Is the mapped annotation itself synthesizable? + return this.mapping.isSynthesizable(); + } + @Override @SuppressWarnings("unchecked") protected A createSynthesizedAnnotation() { @@ -347,22 +358,15 @@ final class TypeMappedAnnotation extends AbstractMergedAnn * Determine if the supplied annotation has not already been synthesized * and whether the mapped annotation is a composed annotation * that needs to have its attributes merged or the mapped annotation is - * {@linkplain AnnotationTypeMapping#isSynthesizable() synthesizable} in general. + * {@linkplain #isSynthesizable() synthesizable} in general. * @param annotation the annotation to check * @since 5.3.22 */ private boolean isSynthesizable(Annotation annotation) { - // Already synthesized? if (annotation instanceof SynthesizedAnnotation) { return false; } - // Is this a mapped annotation for a composed annotation, and are there - // annotation attributes (mirrors) that need to be merged? - if (getDistance() > 0 && this.resolvedMirrors.length > 0) { - return true; - } - // Is the mapped annotation itself synthesizable? - return this.mapping.isSynthesizable(); + return isSynthesizable(); } @Override diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index 0b78f39e378..94efc1e2796 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -1504,6 +1504,13 @@ class MergedAnnotationsTests { assertThat(synthesizedComponent.value()).isEqualTo("webController"); } + @Test + void isSynthesizableWithoutAttributeAliases() throws Exception { + Component component = WebController.class.getAnnotation(Component.class); + assertThat(component).isNotNull(); + assertThat(MergedAnnotation.from(component).isSynthesizable()).isFalse(); + } + @Test void synthesizeAlreadySynthesized() throws Exception { Method method = WebController.class.getMethod("handleMappedWithValueAttribute"); @@ -1567,10 +1574,16 @@ class MergedAnnotationsTests { void synthesizeShouldNotSynthesizeNonsynthesizableAnnotationsWhenUsingMergedAnnotationsFromApi() { MergedAnnotations mergedAnnotations = MergedAnnotations.from(SecurityConfig.class); - EnableWebSecurity enableWebSecurity = mergedAnnotations.get(EnableWebSecurity.class).synthesize(); + MergedAnnotation enableWebSecurityAnnotation = + mergedAnnotations.get(EnableWebSecurity.class); + assertThat(enableWebSecurityAnnotation.isSynthesizable()).isFalse(); + EnableWebSecurity enableWebSecurity = enableWebSecurityAnnotation.synthesize(); assertThat(enableWebSecurity).isNotInstanceOf(SynthesizedAnnotation.class); - EnableGlobalAuthentication enableGlobalAuthentication = mergedAnnotations.get(EnableGlobalAuthentication.class).synthesize(); + MergedAnnotation enableGlobalAuthenticationMergedAnnotation = + mergedAnnotations.get(EnableGlobalAuthentication.class); + assertThat(enableGlobalAuthenticationMergedAnnotation.isSynthesizable()).isFalse(); + EnableGlobalAuthentication enableGlobalAuthentication = enableGlobalAuthenticationMergedAnnotation.synthesize(); assertThat(enableGlobalAuthentication).isNotInstanceOf(SynthesizedAnnotation.class); } @@ -1718,8 +1731,9 @@ class MergedAnnotationsTests { ImplicitAliasesTestConfiguration config = clazz.getAnnotation( ImplicitAliasesTestConfiguration.class); assertThat(config).isNotNull(); - ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from( - config).synthesize(); + MergedAnnotation mergedAnnotation = MergedAnnotation.from(config); + assertThat(mergedAnnotation.isSynthesizable()).isTrue(); + ImplicitAliasesTestConfiguration synthesized = mergedAnnotation.synthesize(); assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class); assertThat(synthesized.value()).isEqualTo(expected); assertThat(synthesized.location1()).isEqualTo(expected); @@ -1746,8 +1760,11 @@ class MergedAnnotationsTests { ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration config = clazz.getAnnotation( ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration.class); assertThat(config).isNotNull(); + MergedAnnotation mergedAnnotation = + MergedAnnotation.from(config); + assertThat(mergedAnnotation.isSynthesizable()).isTrue(); ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration synthesized = - MergedAnnotation.from(config).synthesize(); + mergedAnnotation.synthesize(); assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class); assertThat(synthesized.value()).isEqualTo(expected); assertThat(synthesized.location()).isEqualTo(expected); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java index 151b31f3ae0..eb391f4c1c9 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java @@ -255,6 +255,11 @@ class MissingMergedAnnotationTests { assertThat(this.missing.getDefaultValue("value", Integer.class)).isEmpty(); } + @Test + void isSynthesizableReturnsFalse() { + assertThat(this.missing.isSynthesizable()).isFalse(); + } + @Test void synthesizeThrowsNoSuchElementException() { assertThatNoSuchElementException().isThrownBy(this.missing::synthesize); From 4f0c8797781a1c74ce2885e17e7d7a07bdcab619 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 16 Aug 2022 14:15:02 +0200 Subject: [PATCH 2/3] Rationalize hints required for annotations This commit updates RuntimeHintsUtils to focus on registering a JDK proxy only as annotations of annotated elements that have at least an introspection hints are visible out-of-the-box. This commit also removes unnecessary hints and adapt `@Reflective` to detect if a hint is required using the introduced MergedAnnotation#isSynthesizable. See gh-28967 --- .../JakartaAnnotationsRuntimeHints.java | 42 ------- .../resources/META-INF/spring/aot.factories | 3 - .../JakartaAnnotationsRuntimeHintsTests.java | 62 ---------- ...ProcessorBeanRegistrationAotProcessor.java | 8 +- .../AsyncAnnotationBeanPostProcessor.java | 15 --- .../ScheduledAnnotationBeanPostProcessor.java | 16 --- ...ssorBeanRegistrationAotProcessorTests.java | 14 +-- .../aot/hint/annotation/Reflective.java | 3 + .../support/CoreAnnotationsRuntimeHints.java | 41 ------- .../aot/hint/support/RuntimeHintsUtils.java | 90 +++++---------- .../resources/META-INF/spring/aot.factories | 1 - .../CoreAnnotationsRuntimeHintsTests.java | 61 ---------- .../hint/support/RuntimeHintsUtilsTests.java | 108 ++++++++---------- .../MessagingAnnotationsRuntimeHints.java | 5 +- .../SimpAnnotationsRuntimeHints.java | 5 +- .../annotation/TransactionRuntimeHints.java | 2 +- .../WebAnnotationsRuntimeHintsRegistrar.java | 15 +-- 17 files changed, 97 insertions(+), 394 deletions(-) delete mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java delete mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java delete mode 100644 spring-core/src/main/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHints.java delete mode 100644 spring-core/src/test/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHintsTests.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java deleted file mode 100644 index 7fffb381819..00000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java +++ /dev/null @@ -1,42 +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.beans.factory.annotation; - -import java.util.stream.Stream; - -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.support.RuntimeHintsUtils; -import org.springframework.util.ClassUtils; - -/** - * {@link RuntimeHintsRegistrar} for Jakarta annotations. - *

Hints are only registered if Jakarta inject is on the classpath. - * - * @author Brian Clozel - */ -class JakartaAnnotationsRuntimeHints implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - if (ClassUtils.isPresent("jakarta.inject.Inject", classLoader)) { - Stream.of("jakarta.inject.Inject", "jakarta.inject.Qualifier").forEach(annotationType -> - RuntimeHintsUtils.registerAnnotation(hints, ClassUtils.resolveClassName(annotationType, classLoader))); - } - } - -} diff --git a/spring-beans/src/main/resources/META-INF/spring/aot.factories b/spring-beans/src/main/resources/META-INF/spring/aot.factories index 11530ced5cd..b95137025d8 100644 --- a/spring-beans/src/main/resources/META-INF/spring/aot.factories +++ b/spring-beans/src/main/resources/META-INF/spring/aot.factories @@ -1,5 +1,2 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor - -org.springframework.aot.hint.RuntimeHintsRegistrar=\ -org.springframework.beans.factory.annotation.JakartaAnnotationsRuntimeHints \ No newline at end of file diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java deleted file mode 100644 index 9e06e5a1176..00000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java +++ /dev/null @@ -1,62 +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.beans.factory.annotation; - - -import jakarta.inject.Inject; -import jakarta.inject.Qualifier; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; -import org.springframework.beans.factory.aot.AotServices; -import org.springframework.util.ClassUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link JakartaAnnotationsRuntimeHints}. - * - * @author Brian Clozel - */ -class JakartaAnnotationsRuntimeHintsTests { - - private final RuntimeHints hints = new RuntimeHints(); - - @BeforeEach - void setup() { - AotServices.factories().load(RuntimeHintsRegistrar.class) - .forEach(registrar -> registrar.registerHints(this.hints, - ClassUtils.getDefaultClassLoader())); - } - - @Test - void jakartaInjectAnnotationHasHints() { - assertThat(RuntimeHintsPredicates.reflection().onType(Inject.class) - .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints); - } - - @Test - void jakartaQualifierAnnotationHasHints() { - assertThat(RuntimeHintsPredicates.reflection().onType(Qualifier.class) - .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints); - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java b/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java index 02a824ae887..7080bcfb0cb 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java @@ -142,9 +142,11 @@ class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistratio } private void registerAnnotationIfNecessary(RuntimeHints hints, AnnotatedElement element) { - MergedAnnotation reflectiveAnnotation = MergedAnnotations.from(element).get(Reflective.class); - if (reflectiveAnnotation.getDistance() > 0) { - RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot().getType()); + MergedAnnotation reflectiveAnnotation = MergedAnnotations.from(element) + .get(Reflective.class); + MergedAnnotation metaSource = reflectiveAnnotation.getMetaSource(); + if (metaSource != null) { + RuntimeHintsUtils.registerAnnotationIfNecessary(hints, metaSource); } } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java index 25e4e9d3f32..4b3cd9415c6 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java @@ -25,14 +25,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.support.RuntimeHintsUtils; import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.task.TaskExecutor; import org.springframework.lang.Nullable; -import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor.AsyncAnnotationRuntimeHints; import org.springframework.util.Assert; import org.springframework.util.function.SingletonSupplier; @@ -67,7 +62,6 @@ import org.springframework.util.function.SingletonSupplier; * @see ScheduledAnnotationBeanPostProcessor */ @SuppressWarnings("serial") -@ImportRuntimeHints(AsyncAnnotationRuntimeHints.class) public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor { /** @@ -160,13 +154,4 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd this.advisor = advisor; } - static class AsyncAnnotationRuntimeHints implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - RuntimeHintsUtils.registerAnnotation(hints, Async.class); - } - - } - } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index cb842bbf7f8..facf5323d03 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -37,9 +37,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.support.RuntimeHintsUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; @@ -58,7 +55,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.MethodIntrospector; import org.springframework.core.Ordered; @@ -68,7 +64,6 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; -import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.ScheduledAnnotationsRuntimeHints; import org.springframework.scheduling.config.CronTask; import org.springframework.scheduling.config.FixedDelayTask; import org.springframework.scheduling.config.FixedRateTask; @@ -111,7 +106,6 @@ import org.springframework.util.StringValueResolver; * @see org.springframework.scheduling.config.ScheduledTaskRegistrar * @see AsyncAnnotationBeanPostProcessor */ -@ImportRuntimeHints(ScheduledAnnotationsRuntimeHints.class) public class ScheduledAnnotationBeanPostProcessor implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor, Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware, @@ -611,14 +605,4 @@ public class ScheduledAnnotationBeanPostProcessor this.registrar.destroy(); } - static class ScheduledAnnotationsRuntimeHints implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - RuntimeHintsUtils.registerAnnotation(hints, Scheduled.class); - RuntimeHintsUtils.registerAnnotation(hints, Schedules.class); - } - - } - } diff --git a/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java b/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java index 5f2dd666c91..cf502e01175 100644 --- a/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java @@ -25,7 +25,6 @@ import java.lang.annotation.Target; import org.junit.jupiter.api.Test; import org.springframework.aot.generate.GenerationContext; -import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.annotation.Reflective; @@ -93,19 +92,18 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests { } @Test - void shouldRegisterAnnotation() { + void shouldNotRegisterAnnotationProxyIfNotNeeded() { process(SampleMethodMetaAnnotatedBean.class); RuntimeHints runtimeHints = this.generationContext.getRuntimeHints(); - assertThat(RuntimeHintsPredicates.reflection().onType(SampleInvoker.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(runtimeHints); assertThat(runtimeHints.proxies().jdkProxies()).isEmpty(); } @Test - void shouldRegisterAnnotationAndProxyWithAliasFor() { + void shouldRegisterAnnotationProxy() { process(SampleMethodMetaAnnotatedBeanWithAlias.class); RuntimeHints runtimeHints = this.generationContext.getRuntimeHints(); - assertThat(RuntimeHintsPredicates.reflection().onType(RetryInvoker.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(runtimeHints); - assertThat(RuntimeHintsPredicates.proxies().forInterfaces(RetryInvoker.class, SynthesizedAnnotation.class)).accepts(runtimeHints); + assertThat(RuntimeHintsPredicates.proxies().forInterfaces( + SampleInvoker.class, SynthesizedAnnotation.class)).accepts(runtimeHints); } @Test @@ -236,7 +234,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests { } - @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) + @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Reflective @@ -246,7 +244,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests { } - @Target({ElementType.METHOD}) + @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @SampleInvoker diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/Reflective.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/Reflective.java index 184536a8845..0dba252455e 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/Reflective.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/Reflective.java @@ -32,6 +32,9 @@ import org.springframework.core.annotation.AliasFor; * the annotated element. By default, a reflection hint is added on the * annotated element so that it can be discovered and invoked if necessary. * + *

A reflection hint is also added if necessary on the annotation that + * directly uses this annotation. + * * @author Stephane Nicoll * @author Sam Brannen * @since 6.0 diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHints.java b/spring-core/src/main/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHints.java deleted file mode 100644 index 6b452a6706e..00000000000 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHints.java +++ /dev/null @@ -1,41 +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.hint.support; - -import java.util.stream.Stream; - -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.core.annotation.AliasFor; -import org.springframework.core.annotation.Order; -import org.springframework.lang.Nullable; - -/** - * {@link RuntimeHintsRegistrar} for core annotations. - * - * @author Phillip Webb - * @since 6.0 - */ -class CoreAnnotationsRuntimeHints implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { - Stream.of(AliasFor.class, Order.class).forEach(annotationType -> - RuntimeHintsUtils.registerAnnotation(hints, annotationType)); - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java index 0679bca86e2..916c9a49206 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java @@ -16,19 +16,9 @@ package org.springframework.aot.hint.support; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.function.Consumer; - -import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeHint; -import org.springframework.aot.hint.TypeHint.Builder; -import org.springframework.aot.hint.annotation.Reflective; import org.springframework.core.annotation.AliasFor; +import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.SynthesizedAnnotation; /** @@ -40,72 +30,48 @@ import org.springframework.core.annotation.SynthesizedAnnotation; */ public abstract class RuntimeHintsUtils { - /** - * A {@link TypeHint} customizer suitable for an annotation. Make sure - * that its attributes are visible. - */ - public static final Consumer ANNOTATION_HINT = hint -> - hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS); - /** * Register the necessary hints so that the specified annotation is visible * at runtime. - *

If an annotation attribute aliases an attribute of another annotation, - * the other annotation is registered as well and a JDK proxy hint is defined - * so that the synthesized annotation can be resolved. * @param hints the {@link RuntimeHints} instance to use * @param annotationType the annotation type * @see SynthesizedAnnotation + * @deprecated as annotation attributes are visible without additional hints */ + @Deprecated public static void registerAnnotation(RuntimeHints hints, Class annotationType) { - registerAnnotation(hints, annotationType, false); + registerSynthesizedAnnotation(hints, annotationType); } /** - * Register the necessary hints so that the specified composable - * annotation is visible at runtime. Use this method rather than the regular - * {@link #registerAnnotation(RuntimeHints, Class)} when the specified - * annotation is meta-annotated, but the meta-annotated annotations do not - * need to be visible. + * Register the necessary hints so that the specified annotation can be + * synthesized at runtime if necessary. Such hints are usually required + * if any of the following apply: + *

+ * Consider using {@link #registerAnnotationIfNecessary(RuntimeHints, MergedAnnotation)} + * that determines if the hints are required. * @param hints the {@link RuntimeHints} instance to use - * @param annotationType the composable annotation type - * @see #registerAnnotation(RuntimeHints, Class) + * @param annotationType the annotation type + * @see SynthesizedAnnotation */ - public static void registerComposableAnnotation(RuntimeHints hints, Class annotationType) { - registerAnnotation(hints, annotationType, true); + public static void registerSynthesizedAnnotation(RuntimeHints hints, Class annotationType) { + hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class); } - private static void registerAnnotation(RuntimeHints hints, Class annotationType, boolean withProxy) { - hints.reflection().registerType(annotationType, ANNOTATION_HINT); - Set> allAnnotations = new LinkedHashSet<>(); - collectAliasedAnnotations(new HashSet<>(), allAnnotations, annotationType); - allAnnotations.forEach(annotation -> { - hints.reflection().registerType(annotation, ANNOTATION_HINT); - hints.proxies().registerJdkProxy(annotation, SynthesizedAnnotation.class); - }); - if (!allAnnotations.isEmpty() || withProxy) { - hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class); - } - } - - private static void collectAliasedAnnotations(Set> seen, Set> types, Class annotationType) { - if (seen.contains(annotationType) || AliasFor.class.equals(annotationType) || - Reflective.class.equals(annotationType)) { - return; - } - seen.add(annotationType); - for (Method method : annotationType.getDeclaredMethods()) { - AliasFor aliasFor = method.getAnnotation(AliasFor.class); - if (aliasFor != null) { - Class annotationAttribute = aliasFor.annotation(); - Class targetAnnotation = (annotationAttribute != Annotation.class - ? annotationAttribute : annotationType); - if (types.add(targetAnnotation)) { - if (!targetAnnotation.equals(annotationType)) { - collectAliasedAnnotations(seen, types, targetAnnotation); - } - } - } + /** + * Determine if the specified annotation can be synthesized at runtime, and + * register the necessary hints accordingly. + * @param hints the {@link RuntimeHints} instance to use + * @param annotation the annotation + * @see #registerSynthesizedAnnotation(RuntimeHints, Class) + */ + public static void registerAnnotationIfNecessary(RuntimeHints hints, MergedAnnotation annotation) { + if (annotation.isSynthesizable()) { + registerSynthesizedAnnotation(hints, annotation.getType()); } } diff --git a/spring-core/src/main/resources/META-INF/spring/aot.factories b/spring-core/src/main/resources/META-INF/spring/aot.factories index 5a4fe9170cc..e1e6672e561 100644 --- a/spring-core/src/main/resources/META-INF/spring/aot.factories +++ b/spring-core/src/main/resources/META-INF/spring/aot.factories @@ -1,3 +1,2 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ -org.springframework.aot.hint.support.CoreAnnotationsRuntimeHints,\ org.springframework.aot.hint.support.SpringFactoriesLoaderRuntimeHints diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHintsTests.java deleted file mode 100644 index 704465d1c74..00000000000 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/CoreAnnotationsRuntimeHintsTests.java +++ /dev/null @@ -1,61 +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.hint.support; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; -import org.springframework.core.annotation.AliasFor; -import org.springframework.core.annotation.Order; -import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.util.ClassUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CoreAnnotationsRuntimeHints}. - * - * @author Phillip Webb - */ -class CoreAnnotationsRuntimeHintsTests { - - private final RuntimeHints hints = new RuntimeHints(); - - @BeforeEach - void setup() { - SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories") - .load(RuntimeHintsRegistrar.class).forEach(registrar -> registrar - .registerHints(this.hints, ClassUtils.getDefaultClassLoader())); - } - - @Test - void aliasForHasHints() { - assertThat(RuntimeHintsPredicates.reflection().onType(AliasFor.class) - .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints); - } - - @Test - void orderAnnotationHasHints() { - assertThat(RuntimeHintsPredicates.reflection().onType(Order.class) - .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints); - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java index b4345a91eaf..919ac575309 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java @@ -23,11 +23,11 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.JdkProxyHint; -import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; import org.springframework.core.annotation.AliasFor; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.SynthesizedAnnotation; import static org.assertj.core.api.Assertions.assertThat; @@ -43,75 +43,45 @@ class RuntimeHintsUtilsTests { private final RuntimeHints hints = new RuntimeHints(); @Test - void registerAnnotationType() { - RuntimeHintsUtils.registerAnnotation(this.hints, SampleInvoker.class); - assertThat(this.hints.reflection().typeHints()).singleElement() - .satisfies(annotationHint(SampleInvoker.class)); - assertThat(this.hints.proxies().jdkProxies()).isEmpty(); + void registerSynthesizedAnnotation() { + RuntimeHintsUtils.registerSynthesizedAnnotation(this.hints, SampleInvoker.class); + assertThat(this.hints.proxies().jdkProxies()).singleElement() + .satisfies(annotationProxy(SampleInvoker.class)); } @Test - void registerComposableAnnotationType() { - RuntimeHintsUtils.registerComposableAnnotation(this.hints, SampleInvoker.class); - assertThat(this.hints.reflection().typeHints()).singleElement() - .satisfies(annotationHint(SampleInvoker.class)); - assertThat(this.hints.proxies().jdkProxies()).singleElement() - .satisfies(annotationProxy(SampleInvoker.class)); + void registerAnnotationIfNecessaryWithNonSynthesizedAnnotation() throws NoSuchFieldException { + MergedAnnotation annotation = MergedAnnotations + .from(TestBean.class.getField("sampleInvoker")).get(SampleInvoker.class); + RuntimeHintsUtils.registerAnnotationIfNecessary(this.hints, annotation); + assertThat(this.hints.proxies().jdkProxies()).isEmpty(); } @Test - void registerAnnotationTypeWithLocalUseOfAliasForRegistersProxy() { - RuntimeHintsUtils.registerAnnotation(this.hints, LocalMapping.class); - assertThat(this.hints.reflection().typeHints()).singleElement() - .satisfies(annotationHint(LocalMapping.class)); + void registerAnnotationIfNecessaryWithLocalAliases() throws NoSuchFieldException { + MergedAnnotation annotation = MergedAnnotations + .from(TestBean.class.getField("localMapping")).get(LocalMapping.class); + RuntimeHintsUtils.registerAnnotationIfNecessary(this.hints, annotation); assertThat(this.hints.proxies().jdkProxies()).singleElement() .satisfies(annotationProxy(LocalMapping.class)); } @Test - void registerAnnotationTypeProxyRegistersJdkProxies() { - RuntimeHintsUtils.registerAnnotation(this.hints, RetryInvoker.class); - assertThat(this.hints.reflection().typeHints()) - .anySatisfy(annotationHint(RetryInvoker.class)) - .anySatisfy(annotationHint(SampleInvoker.class)) - .hasSize(2); - assertThat(this.hints.proxies().jdkProxies()) - .anySatisfy(annotationProxy(RetryInvoker.class)) - .anySatisfy(annotationProxy(SampleInvoker.class)) - .hasSize(2); - } - - @Test // gh-28953 - void registerAnnotationForAliasForShouldNotRegisterSynthesizedAnnotationProxy() { - RuntimeHintsUtils.registerAnnotation(this.hints, AliasFor.class); - assertThat(this.hints.reflection().typeHints()).singleElement() - .satisfies(annotationHint(AliasFor.class)); - assertThat(this.hints.proxies().jdkProxies()).isEmpty(); + void registerAnnotationIfNecessaryWithMetaAttributeOverride() throws NoSuchFieldException { + MergedAnnotation annotation = MergedAnnotations + .from(TestBean.class.getField("retryInvoker")).get(SampleInvoker.class); + RuntimeHintsUtils.registerAnnotationIfNecessary(this.hints, annotation); + assertThat(this.hints.proxies().jdkProxies()).singleElement() + .satisfies(annotationProxy(SampleInvoker.class)); } @Test - void registerAnnotationTypeWhereUsedAsAMetaAnnotationRegistersHierarchy() { - RuntimeHintsUtils.registerAnnotation(this.hints, RetryWithEnabledFlagInvoker.class); - assertThat(this.hints.reflection().typeHints()) - .anySatisfy(annotationHint(RetryWithEnabledFlagInvoker.class)) - .anySatisfy(annotationHint(RetryInvoker.class)) - .anySatisfy(annotationHint(SampleInvoker.class)) - .hasSize(3); - assertThat(this.hints.proxies().jdkProxies()) - .anySatisfy(annotationProxy(RetryWithEnabledFlagInvoker.class)) - .anySatisfy(annotationProxy(RetryInvoker.class)) - .anySatisfy(annotationProxy(SampleInvoker.class)) - .hasSize(3); - } - - private Consumer annotationHint(Class type) { - return typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(type)); - assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.fields()).isEmpty(); - assertThat(typeHint.methods()).isEmpty(); - assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS); - }; + void registerAnnotationIfNecessaryWithSynthesizedAttribute() throws NoSuchFieldException { + MergedAnnotation annotation = MergedAnnotations + .from(TestBean.class.getField("retryContainer")).get(RetryContainer.class); + RuntimeHintsUtils.registerAnnotationIfNecessary(this.hints, annotation); + assertThat(this.hints.proxies().jdkProxies()).singleElement() + .satisfies(annotationProxy(RetryContainer.class)); } private Consumer annotationProxy(Class type) { @@ -120,6 +90,22 @@ class RuntimeHintsUtilsTests { } + static class TestBean { + + @SampleInvoker + public String sampleInvoker; + + @LocalMapping + public String localMapping; + + @RetryInvoker + public String retryInvoker; + + @RetryContainer(retry = @RetryInvoker(3)) + public String retryContainer; + + } + @Retention(RetentionPolicy.RUNTIME) @interface LocalMapping { @@ -149,13 +135,9 @@ class RuntimeHintsUtilsTests { } @Retention(RetentionPolicy.RUNTIME) - @RetryInvoker - @interface RetryWithEnabledFlagInvoker { - - @AliasFor(annotation = RetryInvoker.class) - int value() default 5; + @interface RetryContainer { - boolean enabled() default true; + RetryInvoker retry(); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java index 28ba86cfa0a..5d0dc97978f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java @@ -35,8 +35,7 @@ public class MessagingAnnotationsRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { - Stream.of(Controller.class, DestinationVariable.class, Header.class, Headers.class, - MessageExceptionHandler.class, MessageMapping.class, Payload.class, SendTo.class).forEach( - annotationType -> RuntimeHintsUtils.registerAnnotation(hints, annotationType)); + Stream.of(Controller.class, Header.class, Headers.class, Payload.class).forEach(annotationType -> + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, annotationType)); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java index f5d262cf6c2..1ea02f7b0c3 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java @@ -16,8 +16,6 @@ package org.springframework.messaging.simp.annotation; -import java.util.stream.Stream; - import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.support.RuntimeHintsUtils; @@ -33,7 +31,6 @@ public class SimpAnnotationsRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - Stream.of(SendToUser.class, SubscribeMapping.class).forEach( - annotationType -> RuntimeHintsUtils.registerAnnotation(hints, annotationType)); + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, SendToUser.class); } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionRuntimeHints.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionRuntimeHints.java index 2bad3f7585e..8f6ad94bd9d 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionRuntimeHints.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionRuntimeHints.java @@ -37,7 +37,7 @@ class TransactionRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - RuntimeHintsUtils.registerAnnotation(hints, Transactional.class); + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, Transactional.class); hints.reflection() .registerTypes(List.of( TypeReference.of(Isolation.class), diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java index 3e6e3e2b5f4..df8886f40df 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java @@ -36,15 +36,12 @@ public final class WebAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRe @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { Stream.of(Controller.class, ControllerAdvice.class, CookieValue.class, - CrossOrigin.class, DeleteMapping.class, ExceptionHandler.class, - GetMapping.class, InitBinder.class, Mapping.class, MatrixVariable.class, - ModelAttribute.class, PatchMapping.class, PathVariable.class, - PostMapping.class, PutMapping.class, RequestAttribute.class, - RequestBody.class, RequestHeader.class, RequestMapping.class, - RequestParam.class, RequestPart.class, ResponseBody.class, - ResponseStatus.class, RestController.class, RestControllerAdvice.class, - SessionAttribute.class, SessionAttributes.class).forEach( - annotationType -> RuntimeHintsUtils.registerAnnotation(hints, annotationType)); + CrossOrigin.class, MatrixVariable.class, ModelAttribute.class, + PathVariable.class, RequestAttribute.class, RequestHeader.class, + RequestMapping.class, RequestParam.class, RequestPart.class, + ResponseStatus.class, SessionAttribute.class, SessionAttributes.class) + .forEach(annotationType -> + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, annotationType)); } } From 494bca727f6c9ae8723fd33780e6fdd1b0a6a528 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 16 Aug 2022 14:22:04 +0200 Subject: [PATCH 3/3] Disable RuntimeHints tests with the agent Tests with the agent are broken now that hints for an annotation is no longer required if the annotated element has an introspection hint. See gh-28967 --- spring-context/spring-context.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spring-context/spring-context.gradle b/spring-context/spring-context.gradle index 1f25b74cc6e..d7e366331b3 100644 --- a/spring-context/spring-context.gradle +++ b/spring-context/spring-context.gradle @@ -1,7 +1,3 @@ -plugins { - id 'org.springframework.build.runtimehints-agent' -} - description = "Spring Context" apply plugin: "kotlin"