From b8c1fc920275d69185e05ca89afb1dbeefe417e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 6 Sep 2022 18:21:34 +0200 Subject: [PATCH] Revert "Remove RuntimeHintsUtils" This reverts commit 3e327f564107d6e74919b5c2f8ef203fd78c4ebd. --- .../BeanFactoryAnnotationsRuntimeHints.java | 37 +++++ .../resources/META-INF/spring/aot.factories | 1 + .../ReflectiveRuntimeHintsRegistrar.java | 13 ++ .../aot/hint/support/RuntimeHintsUtils.java | 95 +++++++++++ .../ReflectiveRuntimeHintsRegistrarTests.java | 17 ++ .../hint/support/RuntimeHintsUtilsTests.java | 150 ++++++++++++++++++ .../MessagingAnnotationsRuntimeHints.java | 43 +++++ .../SimpAnnotationsRuntimeHints.java | 38 +++++ .../AbstractMessageBrokerConfiguration.java | 4 + .../context/aot/TestAotProcessorTests.java | 3 +- .../annotation/TransactionRuntimeHints.java | 3 + .../WebAnnotationsRuntimeHintsRegistrar.java | 48 ++++++ .../config/WebFluxConfigurationSupport.java | 3 + .../WebMvcConfigurationSupport.java | 3 + 14 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationsRuntimeHints.java create mode 100644 spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java create mode 100644 spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java create mode 100644 spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java create mode 100644 spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java create mode 100644 spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationsRuntimeHints.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationsRuntimeHints.java new file mode 100644 index 00000000000..ec7ad57fdb8 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationsRuntimeHints.java @@ -0,0 +1,37 @@ +/* + * 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 org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.support.RuntimeHintsUtils; +import org.springframework.lang.Nullable; + +/** + * {@link RuntimeHintsRegistrar} implementation for bean factory annotations. + * + * @author Stephane Nicoll + * @since 6.0 + */ +class BeanFactoryAnnotationsRuntimeHints implements RuntimeHintsRegistrar { + + @Override + @SuppressWarnings("deprecation") + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, Qualifier.class); + } +} 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 fd9edf06871..c68e89fda2f 100644 --- a/spring-beans/src/main/resources/META-INF/spring/aot.factories +++ b/spring-beans/src/main/resources/META-INF/spring/aot.factories @@ -1,4 +1,5 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ +org.springframework.beans.factory.annotation.BeanFactoryAnnotationsRuntimeHints,\ org.springframework.beans.factory.annotation.JakartaAnnotationsRuntimeHints org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java index 58b42a424c7..a8bcceb292a 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java @@ -28,6 +28,8 @@ import java.util.function.Consumer; import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.support.RuntimeHintsUtils; +import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -60,9 +62,20 @@ public class ReflectiveRuntimeHintsRegistrar { entries.forEach(entry -> { AnnotatedElement element = entry.element(); entry.processor().registerReflectionHints(runtimeHints.reflection(), element); + registerAnnotationIfNecessary(runtimeHints, element); }); } + @SuppressWarnings("deprecation") + private void registerAnnotationIfNecessary(RuntimeHints hints, AnnotatedElement element) { + MergedAnnotation reflectiveAnnotation = MergedAnnotations.from(element) + .get(Reflective.class); + MergedAnnotation metaSource = reflectiveAnnotation.getMetaSource(); + if (metaSource != null) { + RuntimeHintsUtils.registerAnnotationIfNecessary(hints, metaSource); + } + } + private void processType(Set entries, Class typeToProcess) { if (isReflective(typeToProcess)) { entries.add(createEntry(typeToProcess)); 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 new file mode 100644 index 00000000000..9668f1d891a --- /dev/null +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java @@ -0,0 +1,95 @@ +/* + * 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.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.core.annotation.AliasFor; +import org.springframework.core.annotation.MergedAnnotation; + +/** + * Utility methods for runtime hints support code. + * + * @author Stephane Nicoll + * @author Sam Brannen + * @since 6.0 + */ +public abstract class RuntimeHintsUtils { + + /** + * A {@link TypeHint} customizer suitable for an annotation. Make sure + * that its attributes are visible. + * @deprecated as annotation attributes are visible without additional hints + */ + @Deprecated + 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. + * @param hints the {@link RuntimeHints} instance to use + * @param annotationType the annotation type + * @deprecated For removal prior to Spring Framework 6.0 + */ + @Deprecated + public static void registerAnnotation(RuntimeHints hints, Class annotationType) { + registerSynthesizedAnnotation(hints, annotationType); + } + + /** + * 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: + *
    + *
  • Use {@link AliasFor} for local aliases
  • + *
  • Has a meta-annotation that uses {@link AliasFor} for attribute overrides
  • + *
  • Has nested annotations or arrays of annotations that are synthesizable
  • + *
+ * Consider using {@link #registerAnnotationIfNecessary(RuntimeHints, MergedAnnotation)} + * that determines if the hints are required. + * @param hints the {@link RuntimeHints} instance to use + * @param annotationType the annotation type + * @deprecated For removal prior to Spring Framework 6.0 + */ + @Deprecated + @SuppressWarnings("deprecation") + public static void registerSynthesizedAnnotation(RuntimeHints hints, Class annotationType) { + hints.proxies().registerJdkProxy(annotationType, + org.springframework.core.annotation.SynthesizedAnnotation.class); + } + + /** + * 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) + * @deprecated For removal prior to Spring Framework 6.0 + */ + @Deprecated + public static void registerAnnotationIfNecessary(RuntimeHints hints, MergedAnnotation annotation) { + if (annotation.isSynthesizable()) { + registerSynthesizedAnnotation(hints, annotation.getType()); + } + } + +} diff --git a/spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java b/spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java index a075fb8261a..5c1057d3bb2 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java @@ -86,6 +86,23 @@ class ReflectiveRuntimeHintsRegistrarTests { .satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed"))); } + @Test + void shouldNotRegisterAnnotationProxyIfNotNeeded() { + process(SampleMethodMetaAnnotatedBean.class); + RuntimeHints runtimeHints = this.runtimeHints; + assertThat(runtimeHints.proxies().jdkProxies()).isEmpty(); + } + + @Test + @SuppressWarnings("deprecation") + void shouldRegisterAnnotationProxy() { + process(SampleMethodMetaAnnotatedBeanWithAlias.class); + RuntimeHints runtimeHints = this.runtimeHints; + assertThat(RuntimeHintsPredicates.proxies() + .forInterfaces(SampleInvoker.class, org.springframework.core.annotation.SynthesizedAnnotation.class)) + .accepts(runtimeHints); + } + @Test void shouldProcessAnnotationOnInterface() { process(SampleMethodAnnotatedBeanWithInterface.class); 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 new file mode 100644 index 00000000000..ca4f576e6ea --- /dev/null +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java @@ -0,0 +1,150 @@ +/* + * 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.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Test; + +import org.springframework.aot.hint.JdkProxyHint; +import org.springframework.aot.hint.RuntimeHints; +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RuntimeHintsUtils}. + * + * @author Stephane Nicoll + * @author Sam Brannen + */ +class RuntimeHintsUtilsTests { + + private final RuntimeHints hints = new RuntimeHints(); + + @Test + @SuppressWarnings("deprecation") + void registerSynthesizedAnnotation() { + RuntimeHintsUtils.registerSynthesizedAnnotation(this.hints, SampleInvoker.class); + assertThat(this.hints.proxies().jdkProxies()).singleElement() + .satisfies(annotationProxy(SampleInvoker.class)); + } + + @Test + @SuppressWarnings("deprecation") + 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 + @SuppressWarnings("deprecation") + 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 + @SuppressWarnings("deprecation") + 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 + @SuppressWarnings("deprecation") + 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)); + } + + @SuppressWarnings("deprecation") + private Consumer annotationProxy(Class type) { + return jdkProxyHint -> assertThat(jdkProxyHint.getProxiedInterfaces()) + .containsExactly(TypeReference.of(type), + TypeReference.of(org.springframework.core.annotation.SynthesizedAnnotation.class)); + } + + + 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 { + + @AliasFor("retries") + int value() default 0; + + @AliasFor("value") + int retries() default 0; + + } + + + @Retention(RetentionPolicy.RUNTIME) + @interface SampleInvoker { + + int retries() default 0; + + } + + @Retention(RetentionPolicy.RUNTIME) + @SampleInvoker + @interface RetryInvoker { + + @AliasFor(attribute = "retries", annotation = SampleInvoker.class) + int value() default 1; + + } + + @Retention(RetentionPolicy.RUNTIME) + @interface RetryContainer { + + 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 new file mode 100644 index 00000000000..0b36fba3492 --- /dev/null +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessagingAnnotationsRuntimeHints.java @@ -0,0 +1,43 @@ +/* + * 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.messaging.handler.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.lang.Nullable; +import org.springframework.stereotype.Controller; + +/** + * {@link RuntimeHintsRegistrar} implementation that makes messaging + * annotations available at runtime. + * + * @author Sebastien Deleuze + * @since 6.0 + */ +public class MessagingAnnotationsRuntimeHints implements RuntimeHintsRegistrar { + + @Override + @SuppressWarnings("deprecation") + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + 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 new file mode 100644 index 00000000000..195489373ba --- /dev/null +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SimpAnnotationsRuntimeHints.java @@ -0,0 +1,38 @@ +/* + * 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.messaging.simp.annotation; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.support.RuntimeHintsUtils; + +/** + * {@link RuntimeHintsRegistrar} implementation that makes Simp annotations + * available at runtime. + * + * @author Sebastien Deleuze + * @since 6.0 + */ +public class SimpAnnotationsRuntimeHints implements RuntimeHintsRegistrar { + + @Override + @SuppressWarnings("deprecation") + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, SendToUser.class); + } + +} diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java index f2a74288502..85a58573e0e 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.event.SmartApplicationListener; import org.springframework.core.task.TaskExecutor; import org.springframework.lang.Nullable; @@ -40,10 +41,12 @@ import org.springframework.messaging.converter.KotlinSerializationJsonMessageCon import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.StringMessageConverter; +import org.springframework.messaging.handler.annotation.MessagingAnnotationsRuntimeHints; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler; import org.springframework.messaging.simp.SimpLogging; import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.messaging.simp.annotation.SimpAnnotationsRuntimeHints; import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler; import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler; import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler; @@ -95,6 +98,7 @@ import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBea * @author Sebastien Deleuze * @since 4.0 */ +@ImportRuntimeHints({ MessagingAnnotationsRuntimeHints.class, SimpAnnotationsRuntimeHints.class }) public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware { private static final String MVC_VALIDATOR_NAME = "mvcValidator"; diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/TestAotProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/TestAotProcessorTests.java index 8c80157cd9d..7c870699ab8 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/TestAotProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/TestAotProcessorTests.java @@ -70,7 +70,8 @@ class TestAotProcessorTests extends AbstractAotTests { assertThat(findFiles(resourceOutput)).contains( "META-INF/native-image/org.example/app-tests/reflect-config.json", - "META-INF/native-image/org.example/app-tests/resource-config.json"); + "META-INF/native-image/org.example/app-tests/resource-config.json", + "META-INF/native-image/org.example/app-tests/proxy-config.json"); } private void copy(Class testClass, Path destination) { 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 f5cdc81adf7..e6b267a9f62 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 @@ -21,6 +21,7 @@ import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; +import org.springframework.aot.hint.support.RuntimeHintsUtils; import org.springframework.transaction.TransactionDefinition; /** @@ -34,7 +35,9 @@ import org.springframework.transaction.TransactionDefinition; class TransactionRuntimeHints implements RuntimeHintsRegistrar { @Override + @SuppressWarnings("deprecation") public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + RuntimeHintsUtils.registerSynthesizedAnnotation(hints, Transactional.class); hints.reflection().registerTypes(TypeReference.listOf( Isolation.class, Propagation.class, TransactionDefinition.class), TypeHint.builtWith(MemberCategory.DECLARED_FIELDS)); 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 new file mode 100644 index 00000000000..02bbd432897 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/WebAnnotationsRuntimeHintsRegistrar.java @@ -0,0 +1,48 @@ +/* + * 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.web.bind.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.lang.Nullable; +import org.springframework.stereotype.Controller; + +/** + * {@link RuntimeHintsRegistrar} implementation that makes web binding + * annotations available at runtime. + * + * @author Stephane Nicoll + * @since 6.0 + */ +public final class WebAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRegistrar { + + @Override + @SuppressWarnings("deprecation") + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + Stream.of(Controller.class, ControllerAdvice.class, CookieValue.class, + 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)); + } + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index 21b08246f26..a90b9ced4b0 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.Order; import org.springframework.core.convert.converter.Converter; @@ -45,6 +46,7 @@ import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean; import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.WebAnnotationsRuntimeHintsRegistrar; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.reactive.DispatcherHandler; @@ -83,6 +85,7 @@ import org.springframework.web.server.i18n.LocaleContextResolver; * @author Brian Clozel * @since 5.0 */ +@ImportRuntimeHints(WebAnnotationsRuntimeHintsRegistrar.class) public class WebFluxConfigurationSupport implements ApplicationContextAware { @Nullable diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 9c38411f17b..eda055e562e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -32,6 +32,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.context.annotation.Lazy; import org.springframework.core.SpringProperties; import org.springframework.core.convert.converter.Converter; @@ -70,6 +71,7 @@ import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBea import org.springframework.web.HttpRequestHandler; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.WebAnnotationsRuntimeHintsRegistrar; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.context.ServletContextAware; import org.springframework.web.cors.CorsConfiguration; @@ -188,6 +190,7 @@ import org.springframework.web.util.pattern.PathPatternParser; * @see EnableWebMvc * @see WebMvcConfigurer */ +@ImportRuntimeHints(WebAnnotationsRuntimeHintsRegistrar.class) public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { /**