Introduce RegisterReflection
This commit introduces a declarative way of registering reflection information for arbitrary types. Types can be specified as a class, a class name, or by annotating the type itself. This existing RegisterReflectionForBinding becomes a specialized version of the new annotation, registering the necessary hints for data binding. Closes gh-29194
This commit is contained in:
@@ -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})
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This annotation can be used as a meta-annotation to customize how hints
|
||||
* are registered against each target class.
|
||||
*
|
||||
* <p>The annotated element can be any bean:
|
||||
* <pre><code class="java">
|
||||
* @Configuration
|
||||
* @RegisterReflection(classes = CustomerEntry.class, memberCategories = PUBLIC_FIELDS)
|
||||
* public class MyConfig {
|
||||
* // ...
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>To register reflection hints for the type itself, only member categories
|
||||
* should be specified:<pre><code class="java">
|
||||
* @Component
|
||||
* @RegisterReflection(memberCategories = INVOKE_PUBLIC_METHODS)
|
||||
* public class MyComponent {
|
||||
* // ...
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>Reflection hints can be registered from a method. In this case, at least
|
||||
* one target class should be specified:<pre><code class="java">
|
||||
* @Component
|
||||
* public class MyComponent {
|
||||
*
|
||||
* @RegisterReflection(classes = CustomerEntry.class, memberCategories = PUBLIC_FIELDS)
|
||||
* CustomerEntry process() { ... }
|
||||
* // ...
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>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.
|
||||
* <p>
|
||||
* 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 {};
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>The annotated element can be a configuration class — for example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre><code class="java">
|
||||
* @Configuration
|
||||
* @RegisterReflectionForBinding({Foo.class, Bar.class})
|
||||
* public class MyConfig {
|
||||
* // ...
|
||||
* }</pre>
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>The annotated element can be any Spring bean class or method — for example:
|
||||
* <p>When the annotated element is a type, the type itself is registered if no
|
||||
* candidates are provided:<pre><code class="java">
|
||||
* @Component
|
||||
* @RegisterReflectionForBinding
|
||||
* public class MyBean {
|
||||
* // ...
|
||||
* }</code></pre>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Service
|
||||
* The annotation can also be specified on a method. In that case, at least one
|
||||
* target class must be specified:<pre><code class="java">
|
||||
* @Component
|
||||
* public class MyService {
|
||||
*
|
||||
* @RegisterReflectionForBinding(Baz.class)
|
||||
* public void process() {
|
||||
* public Baz process() {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>The annotated element can also be any test class that uses the <em>Spring
|
||||
* TestContext Framework</em> 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 {
|
||||
* <p>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 {};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Class<?>> allClassNames = new ArrayList<>();
|
||||
allClassNames.addAll(Arrays.asList(annotation.classes()));
|
||||
allClassNames.addAll(Arrays.stream(annotation.classNames())
|
||||
.map(this::loadClass).filter(Objects::nonNull).toList());
|
||||
if (allClassNames.isEmpty()) {
|
||||
if (element instanceof Class<?> clazz) {
|
||||
allClassNames.add(clazz);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("At least one class must be specified, "
|
||||
+ "could not detect target from '" + element + "'");
|
||||
}
|
||||
}
|
||||
return new ReflectionRegistration(allClassNames.toArray(new Class<?>[0]),
|
||||
annotation.memberCategories());
|
||||
}
|
||||
|
||||
protected void registerReflectionHints(ReflectionHints hints, ReflectionRegistration registration) {
|
||||
for (Class<?> target : registration.classes) {
|
||||
registerReflectionHints(hints, target, registration.memberCategories);
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerReflectionHints(ReflectionHints hints, Class<?> target, MemberCategory[] memberCategories) {
|
||||
hints.registerType(target, type -> type.withMembers(memberCategories));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Class<?> loadClass(String className) {
|
||||
try {
|
||||
return ClassUtils.forName(className, getClass().getClassLoader());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Ignoring '" + className + "': " + ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected record ReflectionRegistration(Class<?>[] classes, MemberCategory[] memberCategories) {}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.FieldHint;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
@@ -118,6 +119,18 @@ class ReflectiveRuntimeHintsRegistrarTests {
|
||||
.satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProcessDifferentAnnotationsOnTypeAndField() {
|
||||
process(SampleTypeAndFieldAnnotatedBean.class);
|
||||
assertThat(this.runtimeHints.reflection().getTypeHint(SampleTypeAndFieldAnnotatedBean.class))
|
||||
.satisfies(typeHint -> {
|
||||
assertThat(typeHint.fields().map(FieldHint::getName)).containsOnly("MESSAGE");
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInvokeCustomProcessor() {
|
||||
process(SampleCustomProcessor.class);
|
||||
@@ -175,6 +188,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
|
||||
static class SampleTypeAndFieldAnnotatedBean {
|
||||
|
||||
@Reflective
|
||||
private static final String MESSAGE = "Hello";
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SampleMethodMetaAnnotatedBean {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
* Tests for {@link RegisterReflectionForBindingProcessor}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class RegisterReflectionForBindingProcessorTests {
|
||||
|
||||
@@ -52,10 +53,11 @@ class RegisterReflectionForBindingProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwExceptionWithoutAnnotationAttributeOnClass() {
|
||||
assertThatThrownBy(() -> processor.registerReflectionHints(hints.reflection(),
|
||||
SampleClassWithoutAnnotationAttribute.class))
|
||||
.isInstanceOf(IllegalStateException.class);
|
||||
void registerReflectionForBindingOnClassItself() {
|
||||
processor.registerReflectionHints(hints.reflection(), SampleClassWithoutAnnotationAttribute.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(SampleClassWithoutAnnotationAttribute.class)).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(String.class)).accepts(hints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(SampleClassWithoutAnnotationAttribute.class, "getName")).accepts(hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,12 +82,16 @@ class RegisterReflectionForBindingProcessorTests {
|
||||
static class SampleClassWithGetter {
|
||||
|
||||
public String getName() {
|
||||
return null;
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterReflectionForBinding
|
||||
static class SampleClassWithoutAnnotationAttribute {
|
||||
|
||||
public String getName() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
static class SampleClassWithoutMethodLevelAnnotationAttribute {
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link RegisterReflectionReflectiveProcessor}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class RegisterReflectionReflectiveProcessorTests {
|
||||
|
||||
private static final List<String> NO_METHODS = Collections.emptyList();
|
||||
|
||||
private final RegisterReflectionReflectiveProcessor processor = new RegisterReflectionReflectiveProcessor();
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
@Nested
|
||||
class AnnotatedTypeTests {
|
||||
|
||||
@Test
|
||||
void registerReflectionWithMemberCategory() {
|
||||
registerReflectionHints(RegistrationSimple.class);
|
||||
assertBasicTypeHint(SimplePojo.class, NO_METHODS, List.of(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerReflectionForMultipleTargets() {
|
||||
registerReflectionHints(RegistrationMultipleTargets.class);
|
||||
assertThat(hints.reflection().typeHints()).allSatisfy(
|
||||
hasOnlyMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertThat(hints.reflection().typeHints().map(TypeHint::getType))
|
||||
.hasSameElementsAs(TypeReference.listOf(Number.class, Double.class, Integer.class, Float.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerReflectionOnTargetClass() {
|
||||
registerReflectionHints(AnnotatedSimplePojo.class);
|
||||
assertBasicTypeHint(AnnotatedSimplePojo.class, NO_METHODS,
|
||||
List.of(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AnnotatedMethodTests {
|
||||
|
||||
@Test
|
||||
void registerReflectionForStaticField() throws NoSuchMethodException {
|
||||
Method method = RegistrationMethod.class.getDeclaredMethod("doReflection");
|
||||
registerReflectionHints(method);
|
||||
assertBasicTypeHint(SimplePojo.class, NO_METHODS, List.of(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerReflectionWithoutTarget() throws NoSuchMethodException {
|
||||
Method method = RegistrationMethodWithoutTarget.class.getDeclaredMethod("doReflection");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> registerReflectionHints(method))
|
||||
.withMessageContaining("At least one class must be specified, could not detect target from '")
|
||||
.withMessageContaining(method.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertBasicTypeHint(Class<?> type, List<String> methodNames, List<MemberCategory> memberCategories) {
|
||||
TypeHint typeHint = getTypeHint(type);
|
||||
assertThat(typeHint.methods()).map(ExecutableHint::getName).hasSameElementsAs(methodNames);
|
||||
assertThat(typeHint.getMemberCategories()).hasSameElementsAs(memberCategories);
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> hasOnlyMemberCategories(MemberCategory... categories) {
|
||||
return typeHint -> {
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(categories);
|
||||
};
|
||||
}
|
||||
|
||||
private TypeHint getTypeHint(Class<?> target) {
|
||||
TypeHint typeHint = hints.reflection().getTypeHint(target);
|
||||
assertThat(typeHint).isNotNull();
|
||||
return typeHint;
|
||||
}
|
||||
|
||||
private void registerReflectionHints(AnnotatedElement annotatedElement) {
|
||||
this.processor.registerReflectionHints(this.hints.reflection(), annotatedElement);
|
||||
}
|
||||
|
||||
@RegisterReflection(classes = SimplePojo.class, memberCategories = MemberCategory.INVOKE_PUBLIC_METHODS)
|
||||
static class RegistrationSimple {}
|
||||
|
||||
@RegisterReflection(classes = { Number.class, Double.class },
|
||||
classNames = { "java.lang.Integer", "java.lang.Float" }, memberCategories = MemberCategory.INVOKE_PUBLIC_METHODS)
|
||||
static class RegistrationMultipleTargets {
|
||||
|
||||
}
|
||||
|
||||
static class RegistrationMethod {
|
||||
|
||||
@RegisterReflection(classes = SimplePojo.class, memberCategories = MemberCategory.INVOKE_DECLARED_METHODS)
|
||||
private void doReflection() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RegistrationMethodWithoutTarget {
|
||||
|
||||
@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
|
||||
private void doReflection() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static class SimplePojo {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
|
||||
static class AnnotatedSimplePojo {
|
||||
|
||||
private String test;
|
||||
|
||||
AnnotatedSimplePojo(String test) {
|
||||
this.test = test;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user