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 02dcdcef6ba..b53af8a82c8 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 @@ -35,8 +35,8 @@ import org.springframework.core.annotation.AliasFor; * @author Stephane Nicoll * @author Sam Brannen * @since 6.0 - * @see SimpleReflectiveProcessor * @see ReflectiveRuntimeHintsRegistrar + * @see RegisterReflection @RegisterReflection * @see RegisterReflectionForBinding @RegisterReflectionForBinding */ @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD}) diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflection.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflection.java new file mode 100644 index 00000000000..fb72fca8a13 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflection.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2024 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.aot.hint.MemberCategory; + +/** + * Register reflection hints against an arbitrary number of target classes. + * + *
When using this annotation directly, only the defined + * {@linkplain #memberCategories() member categories} are registered for each + * target class. The target classes can be specified by class or class names. + * When both are specified, they are all considered. If no target class is + * specified, the current class is used. + * + *
This annotation can be used as a meta-annotation to customize how hints + * are registered against each target class. + * + *
The annotated element can be any bean: + *
+ * @Configuration
+ * @RegisterReflection(classes = CustomerEntry.class, memberCategories = PUBLIC_FIELDS)
+ * public class MyConfig {
+ * // ...
+ * }
+ *
+ * To register reflection hints for the type itself, only member categories + * should be specified:
+ * @Component
+ * @RegisterReflection(memberCategories = INVOKE_PUBLIC_METHODS)
+ * public class MyComponent {
+ * // ...
+ * }
+ *
+ * Reflection hints can be registered from a method. In this case, at least + * one target class should be specified:
+ * @Component
+ * public class MyComponent {
+ *
+ * @RegisterReflection(classes = CustomerEntry.class, memberCategories = PUBLIC_FIELDS)
+ * CustomerEntry process() { ... }
+ * // ...
+ * }
+ *
+ * If the class is not available, {@link #classNames()} allows to specify the + * fully qualified name, rather than the {@link Class} reference. + * + * @author Stephane Nicoll + * @since 6.2 + */ +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Reflective(RegisterReflectionReflectiveProcessor.class) +public @interface RegisterReflection { + + /** + * Classes for which reflection hints should be registered. Consider using + * {@link #classNames()} for classes that are not public in the current + * scope. If both {@code classes} and {@code classNames} are specified, they + * are merged in a single set. + *
+ * By default, the annotated type is the target of the registration. When + * placed on a method, at least one class must be specified. + * @see #classNames() + */ + Class>[] classes() default {}; + + /** + * Alternative to {@link #classes()} to specify the classes as class names. + * @see #classes() + */ + String[] classNames() default {}; + + /** + * Specify the {@linkplain MemberCategory member categories} to enable. + */ + MemberCategory[] memberCategories() default {}; + +} diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBinding.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBinding.java index ea0077c9eb6..c91ab40d4d7 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBinding.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBinding.java @@ -25,53 +25,61 @@ import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; /** - * Indicates that the classes specified in the annotation attributes require some - * reflection hints for binding or reflection-based serialization purposes. For each - * class specified, hints on constructors, fields, properties, record components, - * including types transitively used on properties and record components are registered. - * At least one class must be specified in the {@code value} or {@code classes} annotation - * attributes. + * Register reflection hints for data binding or reflection-based serialization + * against an arbitrary number of target classes. * - *
The annotated element can be a configuration class — for example: + *
For each class hints are registered for constructors, fields, properties, + * and record components. Hints are also registered for types transitively used + * on properties and record components. * - *
+ ** - *The annotated element can be a configuration class — for example: + *
+ * }* @Configuration * @RegisterReflectionForBinding({Foo.class, Bar.class}) * public class MyConfig { * // ... - * }
The annotated element can be any Spring bean class or method — for example: + *
When the annotated element is a type, the type itself is registered if no + * candidates are provided:
+ * @Component
+ * @RegisterReflectionForBinding
+ * public class MyBean {
+ * // ...
+ * }
*
- * - * @Service + * The annotation can also be specified on a method. In that case, at least one + * target class must be specified:* *+ * }+ * @Component * public class MyService { * * @RegisterReflectionForBinding(Baz.class) - * public void process() { + * public Baz process() { * // ... * } * - * }
The annotated element can also be any test class that uses the Spring * TestContext Framework to load an {@code ApplicationContext}. * * @author Sebastien Deleuze + * @author Stephane Nicoll * @since 6.0 * @see org.springframework.aot.hint.BindingReflectionHintsRegistrar - * @see Reflective @Reflective + * @see RegisterReflection @RegisterReflection */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented +@RegisterReflection @Reflective(RegisterReflectionForBindingProcessor.class) public @interface RegisterReflectionForBinding { /** * Alias for {@link #classes()}. */ - @AliasFor("classes") + @AliasFor(annotation = RegisterReflection.class, attribute = "classes") Class>[] value() default {}; /** @@ -79,7 +87,14 @@ public @interface RegisterReflectionForBinding { *
At least one class must be specified either via {@link #value} or {@code classes}.
* @see #value()
*/
- @AliasFor("value")
+ @AliasFor(annotation = RegisterReflection.class, attribute = "classes")
Class>[] classes() default {};
+ /**
+ * Alternative to {@link #classes()} to specify the classes as class names.
+ * @see #classes()
+ */
+ @AliasFor(annotation = RegisterReflection.class, attribute = "classNames")
+ String[] classNames() default {};
+
}
diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBindingProcessor.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBindingProcessor.java
index 06e79ee53af..c87f6a4e409 100644
--- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBindingProcessor.java
+++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionForBindingProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -16,39 +16,28 @@
package org.springframework.aot.hint.annotation;
-import java.lang.reflect.AnnotatedElement;
-
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
+import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
-import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.util.Assert;
/**
* A {@link ReflectiveProcessor} implementation that registers reflection hints
- * for data binding purpose (class, constructors, fields, properties, record
- * components, including types transitively used on properties and record components).
+ * for data binding purpose, that is class, constructors, fields, properties,
+ * record components, including types transitively used on properties and record
+ * components.
*
* @author Sebastien Deleuze
+ * @author Stephane Nicoll
* @since 6.0
* @see RegisterReflectionForBinding @RegisterReflectionForBinding
*/
-public class RegisterReflectionForBindingProcessor implements ReflectiveProcessor {
+class RegisterReflectionForBindingProcessor extends RegisterReflectionReflectiveProcessor {
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
-
@Override
- public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
- RegisterReflectionForBinding registerReflection =
- AnnotationUtils.getAnnotation(element, RegisterReflectionForBinding.class);
- if (registerReflection != null) {
- Class>[] classes = registerReflection.classes();
- Assert.state(classes.length != 0, () -> "A least one class should be specified in " +
- "@RegisterReflectionForBinding attributes, and none was provided on " + element);
- for (Class> type : classes) {
- this.bindingRegistrar.registerReflectionHints(hints, type);
- }
- }
+ protected void registerReflectionHints(ReflectionHints hints, Class> target, MemberCategory[] memberCategories) {
+ this.bindingRegistrar.registerReflectionHints(hints, target);
}
}
diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionReflectiveProcessor.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionReflectiveProcessor.java
new file mode 100644
index 00000000000..2308f0a42a8
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/RegisterReflectionReflectiveProcessor.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2002-2024 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.annotation;
+
+import java.lang.reflect.AnnotatedElement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.ReflectionHints;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+
+/**
+ * A {@link ReflectiveProcessor} implementation that pairs with
+ * {@link RegisterReflection @RegisterReflection}. Can be used as a base
+ * implementation for composed annotations that are meta-annotated with
+ * {@link RegisterReflection}.
+ *
+ * @author Stephane Nicoll
+ * @since 6.2
+ */
+public class RegisterReflectionReflectiveProcessor implements ReflectiveProcessor {
+
+ private static final Log logger = LogFactory.getLog(RegisterReflectionReflectiveProcessor.class);
+
+ @Override
+ public final void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
+ RegisterReflection annotation = AnnotatedElementUtils.getMergedAnnotation(
+ element, RegisterReflection.class);
+ Assert.notNull(annotation, "Element must be annotated with @" + RegisterReflection.class.getSimpleName()
+ + ": " + element);
+ ReflectionRegistration registration = parse(element, annotation);
+ registerReflectionHints(hints, registration);
+ }
+
+ protected ReflectionRegistration parse(AnnotatedElement element, RegisterReflection annotation) {
+ List