Add RuntimeHints predicates generator
The `RuntimeHints` API allows to describe hints for the reflection, proxies and resources behavior at runtime. The need for a particular behavior can be covered by several types of hints, at different levels. This knowledge can be important in several cases: * before contributing additional hints, infrastructure can check if an existing hint already covers the behavior * this can be used in test suites and test infrastructure This commit adds a new RuntimeHintsPredicates that generates `Predicate` instances for testing `RuntimeHints` against a desired runtime behavior for reflection, resources or proxies. Closes gh-28555
This commit is contained in:
parent
100ce9642a
commit
9c9b2356ce
|
|
@ -28,8 +28,8 @@ import javax.lang.model.element.Modifier;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
|
@ -277,15 +277,8 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
|||
}
|
||||
|
||||
private void assertHasMethodInvokeHints(Class<?> beanType, String... methodNames) {
|
||||
assertThat(this.hints.reflection().getTypeHint(beanType)).satisfies(typeHint -> {
|
||||
for (String methodName : methodNames) {
|
||||
assertThat(typeHint.methods()).anySatisfy(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo(methodName);
|
||||
assertThat(methodHint.getModes())
|
||||
.containsExactly(ExecutableMode.INVOKE);
|
||||
});
|
||||
}
|
||||
});
|
||||
assertThat(methodNames).allMatch(methodName ->
|
||||
RuntimeHintsPredicates.reflection().onMethod(beanType, methodName).invoke().test(this.hints));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.springframework.aot.generate.GenerationContext;
|
|||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
|
|
@ -96,8 +97,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
|||
void shouldRegisterAnnotation() {
|
||||
process(SampleMethodMetaAnnotatedBean.class);
|
||||
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
|
||||
assertThat(runtimeHints.reflection().getTypeHint(SampleInvoker.class)).satisfies(typeHint ->
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(SampleInvoker.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(runtimeHints);
|
||||
assertThat(runtimeHints.proxies().jdkProxies()).isEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +105,8 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
|||
void shouldRegisterAnnotationAndProxyWithAliasFor() {
|
||||
process(SampleMethodMetaAnnotatedBeanWithAlias.class);
|
||||
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
|
||||
assertThat(runtimeHints.reflection().getTypeHint(RetryInvoker.class)).satisfies(typeHint ->
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(runtimeHints.proxies().jdkProxies()).anySatisfy(jdkProxyHint ->
|
||||
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(
|
||||
TypeReference.of(RetryInvoker.class), TypeReference.of(SynthesizedAnnotation.class)));
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(RetryInvoker.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(runtimeHints);
|
||||
assertThat(RuntimeHintsPredicates.proxies().forInterfaces(RetryInvoker.class, SynthesizedAnnotation.class)).accepts(runtimeHints);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -196,7 +193,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
|||
|
||||
}
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Reflective
|
||||
|
|
@ -206,7 +203,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
|||
|
||||
}
|
||||
|
||||
@Target({ ElementType.METHOD })
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@SampleInvoker
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Generator of {@link ProxyHints} predicates, testing whether the given hints
|
||||
* match the expected behavior for proxies.
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ProxyHintsPredicates {
|
||||
|
||||
ProxyHintsPredicates() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a {@link org.springframework.aot.hint.JdkProxyHint}
|
||||
* is registered for the given interfaces.
|
||||
* <p>Note that the order in which interfaces are given matters.
|
||||
* @param interfaces the proxied interfaces
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
* @see java.lang.reflect.Proxy
|
||||
*/
|
||||
public Predicate<RuntimeHints> forInterfaces(Class<?>... interfaces) {
|
||||
Assert.notEmpty(interfaces, "'interfaces' should not be empty");
|
||||
return forInterfaces(Arrays.stream(interfaces).map(TypeReference::of).toArray(TypeReference[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a {@link org.springframework.aot.hint.JdkProxyHint}
|
||||
* is registered for the given interfaces.
|
||||
* <p>Note that the order in which interfaces are given matters.
|
||||
* @param interfaces the proxied interfaces as type references
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
* @see java.lang.reflect.Proxy
|
||||
*/
|
||||
public Predicate<RuntimeHints> forInterfaces(TypeReference... interfaces) {
|
||||
Assert.notEmpty(interfaces, "'interfaces' should not be empty");
|
||||
return hints -> hints.proxies().jdkProxies().anyMatch(proxyHint ->
|
||||
proxyHint.getProxiedInterfaces().equals(Arrays.asList(interfaces)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Generator of {@link ReflectionHints} predicates, testing whether the given hints
|
||||
* match the expected behavior for reflection.
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ReflectionHintsPredicates {
|
||||
|
||||
ReflectionHintsPredicates() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the given type.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param typeReference the type
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public TypeHintPredicate onType(TypeReference typeReference) {
|
||||
Assert.notNull(typeReference, "'typeReference' should not be null");
|
||||
return new TypeHintPredicate(typeReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the given type.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param type the type
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public TypeHintPredicate onType(Class<?> type) {
|
||||
Assert.notNull(type, "'type' should not be null");
|
||||
return new TypeHintPredicate(TypeReference.of(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the given constructor.
|
||||
* By default, both introspection and invocation hints match.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param constructor the constructor
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public ConstructorHintPredicate onConstructor(Constructor<?> constructor) {
|
||||
Assert.notNull(constructor, "'constructor' should not be null");
|
||||
return new ConstructorHintPredicate(constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the given method.
|
||||
* By default, both introspection and invocation hints match.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param method the method
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public MethodHintPredicate onMethod(Method method) {
|
||||
Assert.notNull(method, "'method' should not be null");
|
||||
return new MethodHintPredicate(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the method that matches the given selector.
|
||||
* This looks up a method on the given type with the expected name, if unique.
|
||||
* By default, both introspection and invocation hints match.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param type the type holding the method
|
||||
* @param methodName the method name
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
* @throws IllegalArgumentException if the method cannot be found or if multiple methods are found with the same name.
|
||||
*/
|
||||
public MethodHintPredicate onMethod(Class<?> type, String methodName) {
|
||||
Assert.notNull(type, "'type' should not be null");
|
||||
Assert.hasText(methodName, "'methodName' should not be null");
|
||||
return new MethodHintPredicate(getMethod(type, methodName));
|
||||
}
|
||||
|
||||
private Method getMethod(Class<?> type, String methodName) {
|
||||
ReflectionUtils.MethodFilter selector = method -> methodName.equals(method.getName());
|
||||
Set<Method> methods = MethodIntrospector.selectMethods(type, selector);
|
||||
if (methods.size() == 1) {
|
||||
return methods.iterator().next();
|
||||
}
|
||||
else if (methods.size() > 1) {
|
||||
throw new IllegalArgumentException(String.format("Found multiple methods named '%s' on class %s", methodName, type.getName()));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("No method named '" + methodName + "' on class " + type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the field that matches the given selector.
|
||||
* This looks up a field on the given type with the expected name, if present.
|
||||
* By default, unsafe or write access are not considered.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param type the type holding the field
|
||||
* @param fieldName the field name
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
* @throws IllegalArgumentException if a field cannot be found with the given name.
|
||||
*/
|
||||
public FieldHintPredicate onField(Class<?> type, String fieldName) {
|
||||
Assert.notNull(type, "'type' should not be null");
|
||||
Assert.hasText(fieldName, "'fieldName' should not be empty");
|
||||
Field field = ReflectionUtils.findField(type, fieldName);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("No field named '" + fieldName + "' on class " + type.getName());
|
||||
}
|
||||
return new FieldHintPredicate(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a reflection hint is registered for the given field.
|
||||
* By default, unsafe or write access are not considered.
|
||||
* <p>The returned type exposes additional methods that refine the predicate behavior.
|
||||
* @param field the field
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public FieldHintPredicate onField(Field field) {
|
||||
Assert.notNull(field, "'field' should not be null");
|
||||
return new FieldHintPredicate(field);
|
||||
}
|
||||
|
||||
public static class TypeHintPredicate implements Predicate<RuntimeHints> {
|
||||
|
||||
private final TypeReference type;
|
||||
|
||||
TypeHintPredicate(TypeReference type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TypeHint getTypeHint(RuntimeHints hints) {
|
||||
return hints.reflection().getTypeHint(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(RuntimeHints hints) {
|
||||
return getTypeHint(hints) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refine the current predicate to only match if the given {@link MemberCategory} is present.
|
||||
* @param memberCategory the member category
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
*/
|
||||
public Predicate<RuntimeHints> withMemberCategory(MemberCategory memberCategory) {
|
||||
Assert.notNull(memberCategory, "'memberCategory' should not be null");
|
||||
return this.and(hints -> getTypeHint(hints).getMemberCategories().contains(memberCategory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refine the current predicate to match if any of the given {@link MemberCategory categories} is present.
|
||||
* @param memberCategories the member categories
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
*/
|
||||
public Predicate<RuntimeHints> withAnyMemberCategory(MemberCategory... memberCategories) {
|
||||
Assert.notEmpty(memberCategories, "'memberCategories' should not be empty");
|
||||
return this.and(hints -> Arrays.stream(memberCategories)
|
||||
.anyMatch(memberCategory -> getTypeHint(hints).getMemberCategories().contains(memberCategory)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract static class ExecutableHintPredicate<T extends Executable> implements Predicate<RuntimeHints> {
|
||||
|
||||
protected final T executable;
|
||||
|
||||
protected ExecutableMode executableMode = ExecutableMode.INTROSPECT;
|
||||
|
||||
ExecutableHintPredicate(T executable) {
|
||||
this.executable = executable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refine the current predicate to match for reflection introspection on the current type.
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
*/
|
||||
public ExecutableHintPredicate<T> introspect() {
|
||||
this.executableMode = ExecutableMode.INTROSPECT;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refine the current predicate to match for reflection invocation on the current type.
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
*/
|
||||
public ExecutableHintPredicate<T> invoke() {
|
||||
this.executableMode = ExecutableMode.INVOKE;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(RuntimeHints runtimeHints) {
|
||||
return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
|
||||
.withAnyMemberCategory(getPublicMemberCategories())
|
||||
.and(hints -> Modifier.isPublic(this.executable.getModifiers())))
|
||||
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())).withAnyMemberCategory(getDeclaredMemberCategories()))
|
||||
.or(exactMatch()).test(runtimeHints);
|
||||
}
|
||||
|
||||
abstract MemberCategory[] getPublicMemberCategories();
|
||||
|
||||
abstract MemberCategory[] getDeclaredMemberCategories();
|
||||
|
||||
abstract Predicate<RuntimeHints> exactMatch();
|
||||
|
||||
/**
|
||||
* Indicate whether the first {@code ExecutableHint} covers the reflection needs for the other one.
|
||||
* For that, both hints must apply to the same member (same type, name and parameters)
|
||||
* and the configured {@code ExecutableMode} of the first must cover the second.
|
||||
*/
|
||||
static boolean includes(ExecutableHint hint, ExecutableHint other) {
|
||||
return hint.getName().equals(other.getName())
|
||||
&& hint.getParameterTypes().equals(other.getParameterTypes())
|
||||
&& (hint.getModes().contains(ExecutableMode.INVOKE)
|
||||
|| !other.getModes().contains(ExecutableMode.INVOKE));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConstructorHintPredicate extends ExecutableHintPredicate<Constructor<?>> {
|
||||
|
||||
ConstructorHintPredicate(Constructor<?> constructor) {
|
||||
super(constructor);
|
||||
}
|
||||
|
||||
MemberCategory[] getPublicMemberCategories() {
|
||||
if (this.executableMode == ExecutableMode.INTROSPECT) {
|
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS};
|
||||
}
|
||||
return new MemberCategory[] {MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS};
|
||||
}
|
||||
|
||||
MemberCategory[] getDeclaredMemberCategories() {
|
||||
if (this.executableMode == ExecutableMode.INTROSPECT) {
|
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS};
|
||||
}
|
||||
return new MemberCategory[] {MemberCategory.INVOKE_DECLARED_CONSTRUCTORS};
|
||||
}
|
||||
|
||||
@Override
|
||||
Predicate<RuntimeHints> exactMatch() {
|
||||
return hints -> hints.reflection().getTypeHint(this.executable.getDeclaringClass()).constructors().anyMatch(executableHint -> {
|
||||
List<TypeReference> parameters = Arrays.stream(this.executable.getParameterTypes()).map(TypeReference::of).toList();
|
||||
ExecutableHint syntheticHint = ExecutableHint.ofConstructor(parameters)
|
||||
.setModes(this.executableMode).build();
|
||||
return includes(executableHint, syntheticHint);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class MethodHintPredicate extends ExecutableHintPredicate<Method> {
|
||||
|
||||
|
||||
MethodHintPredicate(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
MemberCategory[] getPublicMemberCategories() {
|
||||
if (this.executableMode == ExecutableMode.INTROSPECT) {
|
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_PUBLIC_METHODS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS};
|
||||
}
|
||||
return new MemberCategory[] {MemberCategory.INVOKE_PUBLIC_METHODS};
|
||||
}
|
||||
|
||||
MemberCategory[] getDeclaredMemberCategories() {
|
||||
|
||||
if (this.executableMode == ExecutableMode.INTROSPECT) {
|
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS};
|
||||
}
|
||||
return new MemberCategory[] {MemberCategory.INVOKE_DECLARED_METHODS};
|
||||
}
|
||||
|
||||
@Override
|
||||
Predicate<RuntimeHints> exactMatch() {
|
||||
return hints -> (hints.reflection().getTypeHint(this.executable.getDeclaringClass()) != null) &&
|
||||
hints.reflection().getTypeHint(this.executable.getDeclaringClass()).methods().anyMatch(executableHint -> {
|
||||
List<TypeReference> parameters = Arrays.stream(this.executable.getParameterTypes()).map(TypeReference::of).toList();
|
||||
ExecutableHint syntheticHint = ExecutableHint.ofMethod(this.executable.getName(), parameters)
|
||||
.setModes(this.executableMode).build();
|
||||
return includes(executableHint, syntheticHint);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class FieldHintPredicate implements Predicate<RuntimeHints> {
|
||||
|
||||
private final Field field;
|
||||
|
||||
private boolean allowWrite;
|
||||
|
||||
private boolean allowUnsafeAccess;
|
||||
|
||||
FieldHintPredicate(Field field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refine the current predicate to match if write access is allowed on the field.
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
* @see FieldHint#isAllowWrite()
|
||||
*/
|
||||
public FieldHintPredicate allowWrite() {
|
||||
this.allowWrite = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refine the current predicate to match if unsafe access is allowed on the field.
|
||||
* @return the refined {@link RuntimeHints} predicate
|
||||
* @see FieldHint#isAllowUnsafeAccess() ()
|
||||
*/
|
||||
public FieldHintPredicate allowUnsafeAccess() {
|
||||
this.allowUnsafeAccess = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(RuntimeHints runtimeHints) {
|
||||
TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass());
|
||||
if (typeHint == null) {
|
||||
return false;
|
||||
}
|
||||
return memberCategoryMatch(typeHint) || exactMatch(typeHint);
|
||||
}
|
||||
|
||||
private boolean memberCategoryMatch(TypeHint typeHint) {
|
||||
if (Modifier.isPublic(this.field.getModifiers())) {
|
||||
return typeHint.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS)
|
||||
|| typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS);
|
||||
}
|
||||
else {
|
||||
return typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean exactMatch(TypeHint typeHint) {
|
||||
return typeHint.fields().anyMatch(fieldHint ->
|
||||
this.field.getName().equals(fieldHint.getName())
|
||||
&& (!this.allowWrite || this.allowWrite == fieldHint.isAllowWrite())
|
||||
&& (!this.allowUnsafeAccess || this.allowUnsafeAccess == fieldHint.isAllowUnsafeAccess()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentLruCache;
|
||||
|
||||
/**
|
||||
* Generator of {@link ResourceHints} predicates, testing whether the given hints
|
||||
* match the expected behavior for resources.
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ResourceHintsPredicates {
|
||||
|
||||
private static final ConcurrentLruCache<String, Pattern> CACHED_RESOURCE_PATTERNS = new ConcurrentLruCache<>(32, Pattern::compile);
|
||||
|
||||
ResourceHintsPredicates() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a resource hint is registered for the given bundle name.
|
||||
* @param bundleName the resource bundle name
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public Predicate<RuntimeHints> forBundle(String bundleName) {
|
||||
Assert.hasText(bundleName, "resource bundle name should not be empty");
|
||||
return runtimeHints -> runtimeHints.resources().resourceBundles()
|
||||
.anyMatch(bundleHint -> bundleName.equals(bundleHint.getBaseName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a resource hint is registered for the given
|
||||
* resource name, located in the given type's package.
|
||||
* <p>For example, {@code forResource(org.example.MyClass, "myResource.txt")}
|
||||
* will match for {@code "/org/example/myResource.txt"}.
|
||||
* @param type the type's package where to look for the resource
|
||||
* @param resourceName the resource name
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public Predicate<RuntimeHints> forResource(TypeReference type, String resourceName) {
|
||||
String absoluteName = resolveAbsoluteResourceName(type, resourceName);
|
||||
return forResource(absoluteName);
|
||||
}
|
||||
|
||||
private String resolveAbsoluteResourceName(TypeReference type, String resourceName) {
|
||||
if (resourceName.startsWith("/")) {
|
||||
return resourceName;
|
||||
}
|
||||
else {
|
||||
return "/" + type.getPackageName().replace('.', '/')
|
||||
+ "/" + resourceName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that checks whether a resource hint is registered for
|
||||
* the given resource name.
|
||||
* @param resourceName the full resource name
|
||||
* @return the {@link RuntimeHints} predicate
|
||||
*/
|
||||
public Predicate<RuntimeHints> forResource(String resourceName) {
|
||||
return hints -> hints.resources().resourcePatterns().reduce(ResourcePatternHints::merge)
|
||||
.map(hint -> {
|
||||
boolean isExcluded = hint.getExcludes().stream()
|
||||
.anyMatch(excluded -> CACHED_RESOURCE_PATTERNS.get(excluded.getPattern()).matcher(resourceName).matches());
|
||||
if (isExcluded) {
|
||||
return false;
|
||||
}
|
||||
return hint.getIncludes().stream()
|
||||
.anyMatch(included -> CACHED_RESOURCE_PATTERNS.get(included.getPattern()).matcher(resourceName).matches());
|
||||
}).orElse(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,6 +22,8 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A collection of {@link ResourcePatternHint} describing whether
|
||||
* resources should be made available at runtime through a matching
|
||||
|
|
@ -43,6 +45,11 @@ public final class ResourcePatternHints {
|
|||
this.excludes = new ArrayList<>(builder.excludes);
|
||||
}
|
||||
|
||||
private ResourcePatternHints(List<ResourcePatternHint> includes, List<ResourcePatternHint> excludes) {
|
||||
this.includes = includes;
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the include patterns to use to identify the resources to match.
|
||||
* @return the include patterns
|
||||
|
|
@ -59,6 +66,16 @@ public final class ResourcePatternHints {
|
|||
return this.excludes;
|
||||
}
|
||||
|
||||
ResourcePatternHints merge(ResourcePatternHints resourcePatternHints) {
|
||||
List<ResourcePatternHint> includes = new ArrayList<>();
|
||||
includes.addAll(this.includes);
|
||||
includes.addAll(resourcePatternHints.includes);
|
||||
List<ResourcePatternHint> excludes = new ArrayList<>();
|
||||
excludes.addAll(this.excludes);
|
||||
excludes.addAll(resourcePatternHints.excludes);
|
||||
return new ResourcePatternHints(includes, excludes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ResourcePatternHints}.
|
||||
|
|
@ -75,7 +92,7 @@ public final class ResourcePatternHints {
|
|||
* @param includes the include patterns (see {@link ResourcePatternHint} documentation)
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder includes(TypeReference reachableType, String... includes) {
|
||||
public Builder includes(@Nullable TypeReference reachableType, String... includes) {
|
||||
List<ResourcePatternHint> newIncludes = Arrays.stream(includes)
|
||||
.map(include -> new ResourcePatternHint(include, reachableType)).toList();
|
||||
this.includes.addAll(newIncludes);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Static generator of predicates that test whether the given {@link RuntimeHints}
|
||||
* instance matches the expected behavior for reflection, resource or proxy generation.
|
||||
* <p>This utility class can be used by {@link RuntimeHintsRegistrar} to conditionally
|
||||
* register hints depending on what's present already. This can also be used as a
|
||||
* testing utility for checking proper registration of hints:
|
||||
* <pre class="code">
|
||||
* Predicate<RuntimeHints> predicate = RuntimeHintsPredicates.reflection().onMethod(MyClass.class, "someMethod").invoke();
|
||||
* assertThat(predicate).accepts(runtimeHints);
|
||||
* </pre>
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public abstract class RuntimeHintsPredicates {
|
||||
|
||||
private static final ReflectionHintsPredicates reflection = new ReflectionHintsPredicates();
|
||||
|
||||
private static final ResourceHintsPredicates resource = new ResourceHintsPredicates();
|
||||
|
||||
private static final ProxyHintsPredicates proxies = new ProxyHintsPredicates();
|
||||
|
||||
|
||||
private RuntimeHintsPredicates() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate generator for {@link ReflectionHints reflection hints}.
|
||||
* @return the predicate generator
|
||||
*/
|
||||
public static ReflectionHintsPredicates reflection() {
|
||||
return reflection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate generator for {@link ResourceHints resource hints}.
|
||||
* @return the predicate generator
|
||||
*/
|
||||
public static ResourceHintsPredicates resource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate generator for {@link ProxyHints proxy hints}.
|
||||
* @return the predicate generator
|
||||
*/
|
||||
public static ProxyHintsPredicates proxies() {
|
||||
return proxies;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ProxyHintsPredicates}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ProxyHintsPredicatesTests {
|
||||
|
||||
private final ProxyHintsPredicates proxy = new ProxyHintsPredicates();
|
||||
|
||||
private RuntimeHints runtimeHints;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.runtimeHints = new RuntimeHints();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailForEmptyInterfacesArray() {
|
||||
assertThatThrownBy(() -> this.proxy.forInterfaces(new Class<?>[] {})).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyForInterfacesMatchesProxyHint() {
|
||||
this.runtimeHints.proxies().registerJdkProxy(FirstTestInterface.class, SecondTestInterface.class);
|
||||
assertPredicateMatches(this.proxy.forInterfaces(FirstTestInterface.class, SecondTestInterface.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyForInterfacesDoesNotMatchProxyHintDifferentOrder() {
|
||||
this.runtimeHints.proxies().registerJdkProxy(SecondTestInterface.class, FirstTestInterface.class);
|
||||
assertPredicateDoesNotMatch(this.proxy.forInterfaces(FirstTestInterface.class, SecondTestInterface.class));
|
||||
}
|
||||
|
||||
interface FirstTestInterface {
|
||||
|
||||
}
|
||||
|
||||
interface SecondTestInterface {
|
||||
|
||||
}
|
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate.test(this.runtimeHints)).isTrue();
|
||||
}
|
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate.test(this.runtimeHints)).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReflectionHintsPredicates}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ReflectionHintsPredicatesTests {
|
||||
|
||||
private static Constructor<?> privateConstructor;
|
||||
|
||||
private static Constructor<?> publicConstructor;
|
||||
|
||||
private final ReflectionHintsPredicates reflection = new ReflectionHintsPredicates();
|
||||
|
||||
private RuntimeHints runtimeHints;
|
||||
|
||||
|
||||
@BeforeAll
|
||||
static void setupAll() throws Exception {
|
||||
privateConstructor = SampleClass.class.getDeclaredConstructor(String.class);
|
||||
publicConstructor = SampleClass.class.getConstructor();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.runtimeHints = new RuntimeHints();
|
||||
}
|
||||
|
||||
// Reflection on type
|
||||
|
||||
@Test
|
||||
void shouldFailForNullType() {
|
||||
assertThatThrownBy(() -> reflection.onType((TypeReference) null)).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reflectionOnClassShouldMatchIntrospection() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> {
|
||||
});
|
||||
assertPredicateMatches(reflection.onType(SampleClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reflectionOnTypeReferenceShouldMatchIntrospection() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> {
|
||||
});
|
||||
assertPredicateMatches(reflection.onType(TypeReference.of(SampleClass.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reflectionOnDifferentClassShouldNotMatchIntrospection() {
|
||||
this.runtimeHints.reflection().registerType(Integer.class, builder -> {
|
||||
});
|
||||
assertPredicateDoesNotMatch(reflection.onType(TypeReference.of(SampleClass.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithMemberCategoryFailsWithNullCategory() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertThatThrownBy(() -> reflection.onType(SampleClass.class).withMemberCategory(null)).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithMemberCategoryMatchesCategory() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateMatches(reflection.onType(SampleClass.class).withMemberCategory(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithMemberCategoryDoesNotMatchOtherCategory() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withMemberCategory(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithAnyMemberCategoryFailsWithNullCategories() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertThatThrownBy(() -> reflection.onType(SampleClass.class).withAnyMemberCategory(new MemberCategory[]{})).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithAnyMemberCategoryMatchesCategory() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateMatches(reflection.onType(SampleClass.class).withAnyMemberCategory(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeWithAnyMemberCategoryDoesNotMatchOtherCategory() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withAnyMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
}
|
||||
|
||||
// Reflection on constructor
|
||||
|
||||
@Test
|
||||
void constructorIntrospectionMatchesConstructorHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorIntrospectionMatchesIntrospectPublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorIntrospectionMatchesInvokePublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorIntrospectionMatchesIntrospectDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorIntrospectionMatchesInvokeDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationDoesNotMatchConstructorHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationMatchesConstructorInvocationHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationDoesNotMatchIntrospectPublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationMatchesInvokePublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationDoesNotMatchIntrospectDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInvocationMatchesInvokeDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorIntrospectionMatchesConstructorHint() {
|
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class));
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorIntrospectionDoesNotMatchIntrospectPublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorIntrospectionDoesNotMatchInvokePublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorIntrospectionMatchesIntrospectDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorIntrospectionMatchesInvokeDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationDoesNotMatchConstructorHint() {
|
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class));
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationMatchesConstructorInvocationHint() {
|
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class));
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)));
|
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationDoesNotMatchIntrospectPublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationDoesNotMatchInvokePublicConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationDoesNotMatchIntrospectDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateConstructorInvocationMatchesInvokeDeclaredConstructors() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke());
|
||||
}
|
||||
|
||||
// Reflection on method
|
||||
|
||||
@Test
|
||||
void methodIntrospectionMatchesMethodHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodIntrospectionMatchesIntrospectPublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodIntrospectionMatchesInvokePublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodIntrospectionMatchesIntrospectDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodIntrospectionMatchesInvokeDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationDoesNotMatchMethodHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationMatchesMethodInvocationHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE)));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationDoesNotMatchIntrospectPublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationMatchesInvokePublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationDoesNotMatchIntrospectDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInvocationMatchesInvokeDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodIntrospectionMatchesMethodHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodIntrospectionDoesNotMatchIntrospectPublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodIntrospectionDoesNotMatchInvokePublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodIntrospectionMatchesIntrospectDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodIntrospectionMatchesInvokeDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationDoesNotMatchMethodHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationMatchesMethodInvocationHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE)));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationDoesNotMatchIntrospectPublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationDoesNotMatchInvokePublicMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationDoesNotMatchIntrospectDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS));
|
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateMethodInvocationMatchesInvokeDeclaredMethods() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
|
||||
}
|
||||
|
||||
// Reflection on field
|
||||
|
||||
@Test
|
||||
void shouldFailForMissingField() {
|
||||
assertThatThrownBy(() -> reflection.onField(SampleClass.class, "missingField")).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldReflectionMatchesFieldHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldWriteReflectionDoesNotMatchFieldHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowWrite());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldUnsafeReflectionDoesNotMatchFieldHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> {
|
||||
}));
|
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldWriteReflectionMatchesFieldHintWithWrite() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
|
||||
typeHint.withField("publicField", fieldHint -> fieldHint.allowWrite(true)));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").allowWrite());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldUnsafeReflectionMatchesFieldHintWithUnsafe() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class,
|
||||
typeHint -> typeHint.withField("publicField", fieldHint -> fieldHint.allowUnsafeAccess(true)));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldReflectionMatchesPublicFieldsHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.PUBLIC_FIELDS));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldReflectionMatchesDeclaredFieldsHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateFieldReflectionMatchesFieldHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("privateField", fieldHint -> {
|
||||
}));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateFieldReflectionDoesNotMatchPublicFieldsHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.PUBLIC_FIELDS));
|
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateFieldReflectionMatchesDeclaredFieldsHint() {
|
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
|
||||
}
|
||||
|
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate).accepts(this.runtimeHints);
|
||||
}
|
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate).rejects(this.runtimeHints);
|
||||
}
|
||||
|
||||
|
||||
static class SampleClass {
|
||||
|
||||
private String privateField;
|
||||
|
||||
public String publicField;
|
||||
|
||||
public SampleClass() {
|
||||
|
||||
}
|
||||
|
||||
private SampleClass(String message) {
|
||||
|
||||
}
|
||||
|
||||
public void publicMethod() {
|
||||
|
||||
}
|
||||
|
||||
private void privateMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReflectionHintsPredicates}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ResourceHintsPredicatesTests {
|
||||
|
||||
private final ResourceHintsPredicates resources = new ResourceHintsPredicates();
|
||||
|
||||
private RuntimeHints runtimeHints;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.runtimeHints = new RuntimeHints();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourcePatternMatchesResourceName() {
|
||||
this.runtimeHints.resources().registerPattern("/test/spring.*");
|
||||
assertPredicateMatches(resources.forResource("/test/spring.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourcePatternDoesNotMatchResourceName() {
|
||||
this.runtimeHints.resources().registerPattern("/test/spring.*");
|
||||
assertPredicateDoesNotMatch(resources.forResource("/test/other.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourcePatternMatchesTypeAndResourceName() {
|
||||
this.runtimeHints.resources().registerPattern("/org/springframework/aot/hint/spring.*");
|
||||
assertPredicateMatches(resources.forResource(TypeReference.of(getClass()), "spring.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourcePatternDoesNotMatchTypeAndResourceName() {
|
||||
this.runtimeHints.resources().registerPattern("/spring.*");
|
||||
assertPredicateDoesNotMatch(resources.forResource(TypeReference.of(getClass()), "spring.properties"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceBundleMatchesBundleName() {
|
||||
this.runtimeHints.resources().registerResourceBundle("spring");
|
||||
assertPredicateMatches(resources.forBundle("spring"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resourceBundleDoesNotMatchBundleName() {
|
||||
this.runtimeHints.resources().registerResourceBundle("spring");
|
||||
assertPredicateDoesNotMatch(resources.forBundle("other"));
|
||||
}
|
||||
|
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate.test(this.runtimeHints)).isTrue();
|
||||
}
|
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) {
|
||||
assertThat(predicate.test(this.runtimeHints)).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,8 +21,8 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
|
@ -49,16 +49,14 @@ class CoreAnnotationsRuntimeHintsRegistrarTests {
|
|||
|
||||
@Test
|
||||
void aliasForHasHints() {
|
||||
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(AliasFor.class)))
|
||||
.satisfies(hint -> assertThat(hint.getMemberCategories())
|
||||
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(AliasFor.class)
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void orderAnnotationHasHints() {
|
||||
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(Order.class)))
|
||||
.satisfies(hint -> assertThat(hint.getMemberCategories())
|
||||
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(Order.class)
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ResourcePatternHint;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.io.support.DummyFactory;
|
||||
import org.springframework.core.io.support.MyDummyFactory1;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
|
@ -51,28 +49,19 @@ class SpringFactoriesLoaderRuntimeHintsRegistrarTests {
|
|||
|
||||
@Test
|
||||
void resourceLocationHasHints() {
|
||||
assertThat(this.hints.resources().resourcePatterns())
|
||||
.anySatisfy(hint -> assertThat(hint.getIncludes()).map(ResourcePatternHint::getPattern)
|
||||
.contains(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION));
|
||||
assertThat(RuntimeHintsPredicates.resource().forResource(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void factoryTypeHasHint() {
|
||||
TypeReference type = TypeReference.of(DummyFactory.class);
|
||||
assertThat(this.hints.reflection().getTypeHint(type))
|
||||
.satisfies(this::expectedHints);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(DummyFactory.class)
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void factoryImplementationHasHint() {
|
||||
TypeReference type = TypeReference.of(MyDummyFactory1.class);
|
||||
assertThat(this.hints.reflection().getTypeHint(type))
|
||||
.satisfies(this::expectedHints);
|
||||
}
|
||||
|
||||
private void expectedHints(TypeHint hint) {
|
||||
assertThat(hint.getMemberCategories())
|
||||
.containsExactly(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(MyDummyFactory1.class)
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import javax.lang.model.element.Modifier;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
|
|
@ -87,11 +87,8 @@ class InjectionCodeGeneratorTests {
|
|||
TestBean bean = new TestBean();
|
||||
Field field = ReflectionUtils.findField(bean.getClass(), "age");
|
||||
this.generator.generateInjectionCode(field, INSTANCE_VARIABLE, CodeBlock.of("$L", 123));
|
||||
assertThat(this.hints.reflection().getTypeHint(TestBean.class))
|
||||
.satisfies(hint -> assertThat(hint.fields()).anySatisfy(fieldHint -> {
|
||||
assertThat(fieldHint.getName()).isEqualTo("age");
|
||||
assertThat(fieldHint.isAllowWrite()).isTrue();
|
||||
}));
|
||||
assertThat(RuntimeHintsPredicates.reflection().onField(TestBean.class, "age").allowWrite())
|
||||
.accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -127,11 +124,8 @@ class InjectionCodeGeneratorTests {
|
|||
TestBeanWithPrivateMethod bean = new TestBeanWithPrivateMethod();
|
||||
Method method = ReflectionUtils.findMethod(bean.getClass(), "setAge", int.class);
|
||||
this.generator.generateInjectionCode(method, INSTANCE_VARIABLE, CodeBlock.of("$L", 123));
|
||||
assertThat(this.hints.reflection().getTypeHint(TestBeanWithPrivateMethod.class))
|
||||
.satisfies(hint -> assertThat(hint.methods()).anySatisfy(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("setAge");
|
||||
assertThat(methodHint.getModes()).contains(ExecutableMode.INVOKE);
|
||||
}));
|
||||
assertThat(RuntimeHintsPredicates.reflection()
|
||||
.onMethod(TestBeanWithPrivateMethod.class, "setAge").invoke()).accepts(this.hints);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
|||
Loading…
Reference in New Issue