feat(MethodScanner): 添加方法扫描器与对应的方法匹配器 (Gitee #I6XUCF)

This commit is contained in:
huangchengxing 2023-05-09 01:24:04 +08:00
parent 90219696ec
commit 175ece5435
7 changed files with 2073 additions and 0 deletions

View File

@ -0,0 +1,75 @@
package org.dromara.hutool.core.reflect.method;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Predicate;
/**
* <p>方法匹配器本身可作为{@link Predicate}校验方法是否匹配指定条件
* 当作为{@link MethodMetadataLookup}使用时
* 若方法符合条件则返回{@link Boolean#TRUE}否则返回{@code null}
*
* @author huangchengxing
* @see MethodMatcherUtils
* @see MethodMetadataLookup
* @since 6.0.0
*/
@FunctionalInterface
public interface MethodMatcher extends MethodMetadataLookup<Boolean>, Predicate<Method> {
/**
* 检查方法是否匹配
*
* @param method 方法
* @return 结果
*/
boolean test(Method method);
/**
* 返回一个组合的条件当且仅当所有条件都符合时才返回{@code true}
* 等同于{@link Predicate#and(Predicate)}.
*
* @param other 其他条件
* @return 条件
* @throws NullPointerException 当other为null时抛出
*/
@Override
default MethodMatcher and(Predicate<? super Method> other) {
Objects.requireNonNull(other);
return t -> test(t) && other.test(t);
}
/**
* 返回一个与此条件相反的条件
*
* @return 条件
*/
@Override
default MethodMatcher negate() {
return t -> !test(t);
}
/**
* 返回一个组合的条件当且仅当任一条件符合时才返回{@code true}
*
* @param other 其他条件
* @return 条件
* @throws NullPointerException 当other为null时抛出
*/
@Override
default MethodMatcher or(Predicate<? super Method> other) {
Objects.requireNonNull(other);
return t -> test(t) || other.test(t);
}
/**
* 检查方法若结果不为{@code null}则认为方法与其匹配
*
* @param method 要检查的方法
* @return 结果
*/
@Override
default Boolean inspect(Method method) {
return test(method) ? Boolean.TRUE : null;
}
}

View File

@ -0,0 +1,598 @@
package org.dromara.hutool.core.reflect.method;
import org.dromara.hutool.core.annotation.AnnotatedElementUtil;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* 方法匹配器工具类用于基于各种预设条件创建方法匹配器用于配合{@link MethodScanner}从各种范围中寻找匹配的方法
*
* @author huangchengxing
* @see MethodMatcher
* @since 6.0.0
*/
public class MethodMatcherUtils {
/**
* <p>创建一个匹配任何方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher alwaysMatch() {
return method -> true;
}
/**
* <p>创建一个方法匹配器
*
* @param predicate 条件
* @return 方法匹配器
*/
public static MethodMatcher of(final Predicate<Method> predicate) {
return predicate::test;
}
/**
* <p>用于组合多个方法匹配器的方法匹配器仅当所有方法匹配器均匹配失败时才认为方法匹配
*
* @param matchers 方法匹配器
* @return 方法匹配器
* @see Stream#noneMatch
*/
public static MethodMatcher noneMatch(final MethodMatcher... matchers) {
return method -> Stream.of(matchers).noneMatch(matcher -> matcher.test(method));
}
/**
* <p>用于组合多个方法匹配器的方法匹配器当任意方法匹配器匹配成功时即认为方法匹配
*
* @param matchers 方法匹配器
* @return 方法匹配器
* @see Stream#anyMatch
*/
public static MethodMatcher anyMatch(final MethodMatcher... matchers) {
return method -> Stream.of(matchers).anyMatch(matcher -> matcher.test(method));
}
/**
* <p>用于组合多个方法匹配器的方法匹配器当所有方法匹配器均匹配成功时才认为方法匹配
*
* @param matchers 方法匹配器
* @return 方法匹配器
* @see Stream#allMatch
*/
public static MethodMatcher allMatch(final MethodMatcher... matchers) {
return method -> Stream.of(matchers).allMatch(matcher -> matcher.test(method));
}
// region ============= 访问修饰符 =============
/**
* <p>用于匹配共有方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher isPublic() {
return forModifiers(Modifier.PUBLIC);
}
/**
* <p>用于匹配静态方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher isStatic() {
return forModifiers(Modifier.STATIC);
}
/**
* <p>用于匹配公共静态方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher isPublicStatic() {
return forModifiers(Modifier.PUBLIC, Modifier.STATIC);
}
/**
* <p>用于具有指定修饰符的方法的方法匹配器
*
* @param modifiers 修饰符
* @return 方法匹配器
*/
public static MethodMatcher forModifiers(final int... modifiers) {
return method -> {
final int methodModifiers = method.getModifiers();
return Arrays.stream(modifiers).allMatch(modifier -> (methodModifiers & modifier) != 0);
};
}
// endregion
// region ============= 注解 =============
/**
* <p>用于匹配被指定注解标注或注解层级结构中存在指定注解的方法的方法匹配器<br />
* 比如指定注解为 {@code @Annotation}则匹配直接被{@code @Annotation}标注的方法
*
* @param annotationType 注解类型
* @return 方法匹配器
* @see AnnotatedElementUtil#isAnnotationPresent
*/
public static MethodMatcher hasDeclaredAnnotation(final Class<? extends Annotation> annotationType) {
return method -> method.isAnnotationPresent(annotationType);
}
/**
* <p>用于匹配被指定注解标注或注解层级结构中存在指定注解的方法的方法匹配器<br />
* 比如指定注解为 {@code @Annotation}则匹配:
* <ul>
* <li>{@code @Annotation}标注的方法</li>
* <li>被带有{@code @Annotation}注解的派生注解标注的方法</li>
* </ul>
*
* @param annotationType 注解类型
* @return 方法匹配器
* @see AnnotatedElementUtil#isAnnotationPresent
*/
public static MethodMatcher hasAnnotation(final Class<? extends Annotation> annotationType) {
return method -> AnnotatedElementUtil.isAnnotationPresent(method, annotationType);
}
/**
* <p>用于匹配声明方法的类的层级接口中存在任意类被指定注解标注或注解层级结构中存在指定注解的方法的方法匹配器<br />
* 比如指定注解为 {@code @Annotation}则匹配:
* <ul>
* <li>声明方法的类被{@code @Annotation}标注的方法</li>
* <li>声明方法的类被带有{@code @Annotation}注解的派生注解标注的方法</li>
* </ul>
*
* @param annotationType 注解类型
* @return 方法匹配器
* @see AnnotatedElementUtil#isAnnotationPresent
*/
public static MethodMatcher hasAnnotationOnDeclaringClass(final Class<? extends Annotation> annotationType) {
return method -> AnnotatedElementUtil.isAnnotationPresent(method.getDeclaringClass(), annotationType);
}
/**
* <p>用于匹配方法本身或声明方法的类上直接被指定注解标注或注解层级结构中存在指定注解的方法的方法匹配器<br />
* 比如指定注解为 {@code @Annotation}则匹配:
* <ul>
* <li>{@code @Annotation}标注的方法</li>
* <li>被带有{@code @Annotation}注解的派生注解标注的方法</li>
* <li>声明方法的类被{@code @Annotation}标注的方法</li>
* <li>声明方法的类被带有{@code @Annotation}注解的派生注解标注的方法</li>
* </ul>
*
* @param annotationType 注解类型
* @return 方法匹配器
*/
public static MethodMatcher hasAnnotationOnMethodOrDeclaringClass(final Class<? extends Annotation> annotationType) {
return method -> AnnotatedElementUtil.isAnnotationPresent(method, annotationType)
|| AnnotatedElementUtil.isAnnotationPresent(method.getDeclaringClass(), annotationType);
}
// endregion
// region ============= getter & setter =============
/**
* <p>用于获得指定属性的getter方法的匹配器
* <ul>
* <li>查找方法名为{@code get + 首字母大写的属性名}的无参数方法</li>
* <li>查找方法名为属性名的无参数方法</li>
* <li>{@code fieldType}{@code boolean}{@code Boolean}则同时查找方法名为{@code is + 首字母大写的属性}的无参数方法;</li>
* </ul>
*
* @param fieldName 属性名
* @param fieldType 属性类型
* @return 方法匹配器
*/
public static MethodMatcher forGetterMethod(final String fieldName, final Class<?> fieldType) {
Objects.requireNonNull(fieldName);
Objects.requireNonNull(fieldType);
// 匹配方法名为 get + 首字母大写的属性名的无参数方法
MethodMatcher nameMatcher = forName(CharSequenceUtil.upperFirstAndAddPre(fieldName, "get"));
// 查找方法名为属性名的无参数方法
nameMatcher = nameMatcher.or(forName(fieldName));
if (Objects.equals(boolean.class, fieldType) || Objects.equals(Boolean.class, fieldType)) {
// 匹配方法名为 get + 首字母大写的属性名的无参数方法
nameMatcher = nameMatcher.or(forName(CharSequenceUtil.upperFirstAndAddPre(fieldName, "is")));
}
return allMatch(nameMatcher, forReturnType(fieldType), forNoneParameter());
}
/**
* <p>用于获得指定属性的getter方法的匹配器
* <ul>
* <li>查找方法名为{@code get + 首字母大写的属性名}的无参数方法</li>
* <li>查找方法名为属性名的无参数方法</li>
* <li>{@code fieldType}{@code boolean}{@code Boolean}则同时查找方法名为{@code is + 首字母大写的属性}的无参数方法;</li>
* </ul>
*
* @param field 属性
* @return 方法匹配器
*/
public static MethodMatcher forGetterMethod(final Field field) {
Objects.requireNonNull(field);
return forGetterMethod(field.getName(), field.getType());
}
/**
* <p>用于获得指定属性的setter方法的匹配器默认查找方法名为{@code set + 首字母大写的属性}的单参数方法
* <ul>
* <li>查找方法名为{@code set + 首字母大写的属性名}的单参数方法</li>
* <li>查找方法名为属性名的单参数方法</li>
* </ul>
*
* @param fieldName 属性名
* @param fieldType 属性类型
* @return 方法匹配器
*/
public static MethodMatcher forSetterMethod(final String fieldName, final Class<?> fieldType) {
Objects.requireNonNull(fieldName);
Objects.requireNonNull(fieldType);
final MethodMatcher nameMatcher = forName(CharSequenceUtil.upperFirstAndAddPre(fieldName, "set"))
.or(forName(fieldName));
return allMatch(nameMatcher, forParameterTypes(fieldType));
}
/**
* <p>用于获得指定属性的setter方法的匹配器默认查找方法名为{@code set + 首字母大写的属性}的单参数方法
* <ul>
* <li>查找方法名为{@code set + 首字母大写的属性名}的单参数方法</li>
* <li>查找方法名为属性名的单参数方法</li>
* </ul>
*
* @param field 属性
* @return 方法匹配器
*/
public static MethodMatcher forSetterMethod(final Field field) {
Objects.requireNonNull(field);
return forSetterMethod(field.getName(), field.getType());
}
// endregion
// region ============= 名称 & 参数类型 =============
/**
* <p>用于同时匹配方法名和参数类型的方法匹配器其中参数类型匹配时允许参数类型为方法参数类型的子类
*
* @param methodName 方法名
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forNameAndParameterTypes(final String methodName, final Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
Objects.requireNonNull(parameterTypes);
return allMatch(forName(methodName), forParameterTypes(parameterTypes));
}
/**
* <p>用于同时匹配方法名和参数类型的方法匹配器其中参数类型匹配时要求参数类型与方法参数类型完全一致
*
* @param methodName 方法名
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forNameAndStrictParameterTypes(final String methodName, final Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
Objects.requireNonNull(parameterTypes);
return allMatch(forName(methodName), forStrictParameterTypes(parameterTypes));
}
/**
* <p>用于同时匹配方法名和参数类型的方法匹配器其中参数类型匹配时允许参数类型为方法参数类型的子类且方法名忽略大小写
*
* @param methodName 方法名
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forNameIgnoreCaseAndParameterTypes(
final String methodName, final Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
Objects.requireNonNull(parameterTypes);
return allMatch(forNameIgnoreCase(methodName), forParameterTypes(parameterTypes));
}
/**
* <p>用于同时匹配方法名和参数类型的方法匹配器其中参数类型匹配时要求参数类型与方法参数类型完全一致且方法名忽略大小写
*
* @param methodName 方法名
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forNameIgnoreCaseAndStrictParameterTypes(
final String methodName, Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
Objects.requireNonNull(parameterTypes);
return allMatch(forNameIgnoreCase(methodName), forStrictParameterTypes(parameterTypes));
}
// endregion
// region ============= 名称 & 返回值类型 & 参数类型 =============
/**
* <p>用于匹配方法签名的方法匹配器检查的内容包括
* <ul>
* <li>方法名是否完全一致</li>
* <li>返回值类型是否匹配允许返回值类型为方法返回值类型的子类</li>
* <li>参数类型是否匹配允许参数类型为方法参数类型的子类</li>
* </ul>
*
* @param method 方法
* @return 方法匹配器
*/
public static MethodMatcher forMethodSignature(final Method method) {
Objects.requireNonNull(method);
return forMethodSignature(method.getName(), method.getReturnType(), method.getParameterTypes());
}
/**
* <p>用于匹配方法签名的方法匹配器检查的内容包括
* <ul>
* <li>方法名是否完全一致</li>
* <li>返回值类型是否匹配允许返回值类型为方法返回值类型的子类若返回值类型为{@code null}则表示匹配无返回值的方法</li>
* <li>参数类型是否匹配允许参数类型为方法参数类型的子类若参数类型为{@code null}则表示匹配无参数的方法</li>
* </ul>
*
* @param methodName 方法名
* @param returnType 返回值类型若为{@code null}则表示匹配无返回值的方法
* @param parameterTypes 参数类型若为{@code null}则表示匹配无参数的方法
* @return 方法匹配器
*/
public static MethodMatcher forMethodSignature(
final String methodName, final Class<?> returnType, final Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
final MethodMatcher resultMatcher = Objects.isNull(returnType) ?
forNoneReturnType() : forReturnType(returnType);
final MethodMatcher parameterMatcher = Objects.isNull(parameterTypes) ?
forNoneParameter() : forParameterTypes(parameterTypes);
return allMatch(forName(methodName), resultMatcher, parameterMatcher);
}
/**
* <p>用于匹配方法签名的方法匹配器检查的内容包括
* <ul>
* <li>方法名是否完全一致</li>
* <li>返回值类型是否匹配要求返回值类型与方法返回值类型完全一致若返回值类型为{@code null}则表示匹配无返回值的方法</li>
* <li>参数类型是否匹配要求参数类型与方法参数类型完全一致若参数类型为{@code null}则表示匹配无参数的方法</li>
* </ul>
*
* @param methodName 方法名
* @param returnType 返回值类型若为{@code null}则表示匹配无返回值的方法
* @param parameterTypes 参数类型若为{@code null}则表示匹配无参数的方法
* @return 方法匹配器
*/
public static MethodMatcher forStrictMethodSignature(
final String methodName, final Class<?> returnType, final Class<?>... parameterTypes) {
Objects.requireNonNull(methodName);
final MethodMatcher resultMatcher = Objects.isNull(returnType) ?
forNoneReturnType() : forReturnType(returnType);
final MethodMatcher parameterMatcher = Objects.isNull(parameterTypes) ?
forNoneParameter() : forStrictParameterTypes(parameterTypes);
return allMatch(forName(methodName), resultMatcher, parameterMatcher);
}
/**
* <p>用于匹配方法签名的方法匹配器检查的内容包括
* <ul>
* <li>方法名是否完全一致</li>
* <li>返回值类型是否匹配要求返回值类型与方法返回值类型完全一致</li>
* <li>参数类型是否匹配要求参数类型与方法参数类型完全一致</li>
* </ul>
*
* @param method 方法
* @return 方法匹配器
*/
public static MethodMatcher forStrictMethodSignature(final Method method) {
Objects.requireNonNull(method);
return forMethodSignature(method.getName(), method.getReturnType(), method.getParameterTypes());
}
// endregion
// region ============= 单条件匹配 =============
/**
* <p>用于根据方法名匹配方法的方法匹配器
*
* @param methodName 方法名
* @return 方法匹配器
*/
public static MethodMatcher forName(final String methodName) {
return method -> Objects.equals(method.getName(), methodName);
}
/**
* <p>用于根据方法名匹配方法的方法匹配器忽略方法名大小写
*
* @param methodName 方法名
* @return 方法匹配器
*/
public static MethodMatcher forNameIgnoreCase(final String methodName) {
return method -> CharSequenceUtil.endWithIgnoreCase(method.getName(), methodName);
}
/**
* <p>用于匹配无返回值的方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher forNoneReturnType() {
return method -> Objects.equals(method.getReturnType(), Void.TYPE);
}
/**
* <p>用于匹配指定参数类型的方法的方法匹配器只要参数类型可以赋值给方法参数类型
*
* @param returnType 返回值类型
* @return 方法匹配器
*/
public static MethodMatcher forReturnType(final Class<?> returnType) {
return method -> ClassUtil.isAssignable(returnType, method.getReturnType());
}
/**
* <p>用于匹配指定返回值类型的方法的方法匹配器要求返回值类型与指定类型完全一致
*
* @param returnType 返回值类型
* @return 方法匹配器
*/
public static MethodMatcher forStrictReturnType(final Class<?> returnType) {
return method -> Objects.equals(method.getReturnType(), returnType);
}
/**
* <p>用于匹配无参数方法的方法匹配器
*
* @return 方法匹配器
*/
public static MethodMatcher forNoneParameter() {
return method -> method.getParameterCount() == 0;
}
/**
* <p>用于匹配指定参数个数的方法的方法匹配器
*
* @param count 参数个数
* @return 方法匹配器
*/
public static MethodMatcher forParameterCount(int count) {
return method -> method.getParameterCount() == count;
}
/**
* <p>用于匹配指定参数类型的方法的方法匹配器只要参数类型可以赋值给方法参数类型即认为匹配成功
* 比如参数类型为{@link java.util.ArrayList}则方法参数类型可以为{@link java.util.List}{@link java.util.Collection}
*
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forParameterTypes(final Class<?>... parameterTypes) {
Objects.requireNonNull(parameterTypes);
return method -> ClassUtil.isAllAssignableFrom(parameterTypes, method.getParameterTypes());
}
/**
* <p>用于匹配指定参数类型的方法的方法匹配器{@link #forParameterTypes}不同的是该方法仅用于尽量可能最匹配的方法
* <ul>
* <li>若参数为空则表示匹配无参数方法</li>
* <li>
* 若参数不为空
* <ul>
* <li>仅匹配{@code parameterTypes}中不为{@code null}的参数类型若参数类型为{@code null}则表示匹配任意类型的参数</li>
* <li>若N为{@code parameterTypes}长度则仅要求{@code parameterTypes}不为{@code null}的类型与方法前N个参数类型匹配即可</li>
* <li>{@code parameterTypes}长度大于参数列表长度则直接返回{@code false}</li>
* </ul>
* </li>
* </ul>
* 比如<br />
* 若存在三参数方法{@code method(String, Integer, Object)}支持以下匹配
* <ul>
* <li>{@code forMostSpecificParameterTypes(CharSequence.class, Number.class, Object.class)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class, Object.class)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class, null)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, null, null)}</li>
* <li>{@code forMostSpecificParameterTypes(null, null, null)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class)}</li>
* </ul>
*
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forMostSpecificParameterTypes(final Class<?>... parameterTypes) {
return mostSpecificStrictParameterTypesMatcher(parameterTypes, ClassUtil::isAssignable);
}
/**
* <p>用于匹配指定参数类型的方法的方法匹配器{@link #forParameterTypes}不同的是该方法仅用于尽量可能最匹配的方法
* <ul>
* <li>若参数为空则表示匹配无参数方法</li>
* <li>
* 若参数不为空
* <ul>
* <li>仅匹配{@code parameterTypes}中不为{@code null}的参数类型若参数类型为{@code null}则表示匹配任意类型的参数</li>
* <li>若N为{@code parameterTypes}长度则仅要求{@code parameterTypes}不为{@code null}的类型与方法前N个参数类型匹配即可</li>
* <li>{@code parameterTypes}长度大于参数列表长度则直接返回{@code false}</li>
* </ul>
* </li>
* </ul>
* 比如<br />
* 若存在三参数方法{@code method(String, Integer, Object)}支持以下匹配
* <ul>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class, Object.class)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class, null)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, null, null)}</li>
* <li>{@code forMostSpecificParameterTypes(null, null, null)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class, Integer.class)}</li>
* <li>{@code forMostSpecificParameterTypes(String.class)}</li>
* </ul>
*
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forMostSpecificStrictParameterTypes(final Class<?>... parameterTypes) {
return mostSpecificStrictParameterTypesMatcher(parameterTypes, Objects::equals);
}
/**
* <p>用于匹配指定参数类型的方法的方法匹配器只有参数类型完全匹配才认为匹配成功
*
* @param parameterTypes 参数类型
* @return 方法匹配器
*/
public static MethodMatcher forStrictParameterTypes(final Class<?>... parameterTypes) {
Objects.requireNonNull(parameterTypes);
return method -> ArrayUtil.equals(method.getParameterTypes(), parameterTypes);
}
// endregion
@NotNull
private static MethodMatcher mostSpecificStrictParameterTypesMatcher(
Class<?>[] parameterTypes, BiPredicate<Class<?>, Class<?>> typeMatcher) {
Objects.requireNonNull(parameterTypes);
// 若参数为空则表示匹配无参数方法
if (parameterTypes.length == 0) {
return forNoneParameter();
}
// 若参数不为空则表示匹配指定参数类型的方法
return method -> {
final Class<?>[] methodParameterTypes = method.getParameterTypes();
if (parameterTypes.length > methodParameterTypes.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
final Class<?> parameterType = parameterTypes[i];
// 若参数类型为null则表示匹配任意类型的参数
if (Objects.isNull(parameterType)) {
continue;
}
// 若参数类型不为null则要求参数类型可以赋值给方法参数类型
if (typeMatcher.negate().test(parameterType, methodParameterTypes[i])) {
return false;
}
}
return true;
};
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.hutool.core.reflect.method;
import java.lang.reflect.Method;
/**
* 方法的元数据查找器参照 spring {@code MethodIntrospector.MetadataLookup}用于从方法上获得特定的元数据
*
* @author huangchengxing
* @see MethodMatcher
* @see MethodMatcherUtils
* @since 6.0.0
*/
@FunctionalInterface
public interface MethodMetadataLookup<T> {
/**
* 检查并返回方法上的特定元数据若结果不为{@code null}则认为方法与其匹配
*
* @param method 要检查的方法
* @return 结果
*/
T inspect(Method method);
}

View File

@ -0,0 +1,373 @@
package org.dromara.hutool.core.reflect.method;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.lang.mutable.Mutable;
import org.dromara.hutool.core.lang.mutable.MutableObj;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.WeakConcurrentMap;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.core.reflect.MethodUtil;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Predicate;
/**
* <p>方法查找工具类用于从指定的类或类层级结构中根据特定规则搜索方法
*
* <p><strong>查找方式与查找范围</strong>
* <p>支持四种语义的查找
* <ul>
* <li><i>get</i>: 获取首个符合条件的方法</li>
* <li><i>getWithMetadata</i>: 获取首个带有指定元数据的方法与该元数据</li>
* <li><i>find</i>: 获得所有符合条件的方法</li>
* <li><i>findWithMetadata</i>: 获取所有带有指定元数据的方法与这些元数据</li>
* </ul>
* 基于上述四种语义的查找提供四种查找范围
* <ul>
* <li><i>xxxFromSpecificMethods</i>在用户给定的方法列表中查找匹配的方法</li>
* <li><i>xxxFromMethods</i>{@link Class#getMethods}的范围中查找匹配的方法</li>
* <li><i>xxxFromDeclaredMethods</i>{@link Class#getDeclaredMethods}的范围中查找匹配的方法</li>
* <li><i>xxxFromAllMethods</i>在类的层级结构中的所有类的所有方法的范围中查找匹配的方法</li>
* </ul>
* 比如我们希望获取{@link Class#getMethods()}中所有匹配条件的方法则应当调用{@link #findFromMethods}
* 若我们希望获得类所有方法范围中首个匹配的方法则应当调用{@link #getFromAllMethods}
*
* <p><strong>匹配规则</strong>
* <p>方法查找的规则由{@link MethodMetadataLookup}实现<br />
* 规定{@link MethodMetadataLookup#inspect(Method)}方法返回元数据不为{@code null}则认为方法与其匹配返回结果时将同时返回匹配的方法与元数据<br />
* 因此我们可以通过实现{@link MethodMetadataLookup}接口来同时实现方法的查找与元数据的获取<br />
* 比如我们希望查找所有方法上带有{@code Annotation}注解的方法则可以实现如下
* <pre>{@code
* Map<Method, Annotation> methods = MethodScanner.findFromAllMethods(Foo.class, method -> method.getAnnotation(Annotation.class));
* }</pre>
* 此外对于一些无需获取元数据的查找我们可以使用{@link MethodMatcherUtils}提供的一些内置实现
* <pre>{@code
* // 查找所有静态公开方法
* Set<Method> methods = MethodScanner.findFromAllMethods(Foo.class, MethodMatcherUtils.isPublicStatic());
* // 按照方法名与参数类型查找方法
* Method method = MethodScanner.getFromAllMethods(Foo.class, MethodMatcherUtils.forNameAndParameterTypes("foo", String.class));
* }</pre>
*
* <p><strong>缓存</strong>
* <p>对于{@link #getDeclaredMethods}{@link #getMethods}方法与基于这两个方法实现的
* 所有{@code xxxFromMethods}{@code xxxFromDeclaredMethods}方法
* 都提供了缓存基于{@link WeakConcurrentMap}的缓存支持<br />
* {@link #getAllMethods}与所有{@code xxxFromAllMethods}方法都基于{@link #getDeclaredMethods}实现
* 但是每次全量查找都需要重新遍历类层级结构因此会带来一定的额外的性能损耗<br />
* 缓存在GC时会被回收但是也可以通过{@link #clearCaches}手动清除缓存
*
* @author huangchengxing
* @see MethodMetadataLookup
* @see MethodMatcher
* @see MethodMatcherUtils
* @since 6.0.0
*/
public class MethodScanner {
/**
* TODO 替换{@link MethodUtil}中的部分方法实现
*/
/**
* 空方法列表
*/
private static final Method[] EMPTY_METHODS = new Method[0];
/**
* 方法缓存
*/
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>();
/**
* 直接声明的方法缓存
*/
private static final WeakConcurrentMap<Class<?>, Method[]> DECLARED_METHODS_CACHE = new WeakConcurrentMap<>();
// region ============= basic =============
/**
* 获取当前类及父类的所有公共方法等同于{@link Class#getMethods()}
*
* @param type
* @return 当前类及父类的所有公共方法
*/
public static Method[] getMethods(Class<?> type) {
if (Objects.isNull(type)) {
return EMPTY_METHODS;
}
return MapUtil.computeIfAbsentForJdk8(METHODS_CACHE, type, Class::getMethods);
}
/**
* 获取当前类直接声明的所有方法等同于{@link Class#getDeclaredMethods()}
*
* @param type
* @return 当前类及父类的所有公共方法
*/
public static Method[] getDeclaredMethods(Class<?> type) {
if (Objects.isNull(type)) {
return EMPTY_METHODS;
}
return MapUtil.computeIfAbsentForJdk8(DECLARED_METHODS_CACHE, type, Class::getDeclaredMethods);
}
/**
* <p>获取当前类层级结构中的所有方法<br>
* 等同于按广度优先遍历类及其所有父类与接口并依次调用{@link Class#getDeclaredMethods()}<br>
* 返回的方法排序规则如下
* <ul>
* <li>{@code type}距离越近则顺序越靠前</li>
* <li>{@code type}距离相同则父类优先于接口</li>
* <li>{@code type}距离相同的接口则顺序遵循接口在{@link Class#getInterfaces()}的顺序</li>
* </ul>
*
* @param type
* @return 当前类及父类的所有公共方法
* @see ClassUtil#traverseTypeHierarchyWhile(Class, Predicate)
*/
public static Method[] getAllMethods(Class<?> type) {
if (Objects.isNull(type)) {
return EMPTY_METHODS;
}
List<Method> methods = new ArrayList<>();
ClassUtil.traverseTypeHierarchyWhile(type, t -> {
methods.addAll(Arrays.asList(getDeclaredMethods(t)));
return true;
});
return methods.isEmpty() ? EMPTY_METHODS : methods.toArray(new Method[0]);
}
/**
* 清空缓存
*/
public static void clearCaches() {
METHODS_CACHE.clear();
DECLARED_METHODS_CACHE.clear();
}
// endregion
// region ============= specific methods =============
/**
* 从指定方法列表中筛选所有方法上带有指定元数据方法的方法与对应元数据
*
* @param methods 方法列表
* @param lookup 查找器
* @return 方法与对应的元数据集合
*/
public static <T> Map<Method, T> findWithMetadataFromSpecificMethods(Method[] methods, MethodMetadataLookup<T> lookup) {
if (ArrayUtil.isEmpty(methods)) {
return Collections.emptyMap();
}
Map<Method, T> results = new LinkedHashMap<>();
for (Method method : methods) {
T result = lookup.inspect(method);
if (Objects.nonNull(result)) {
results.put(method, result);
}
}
return results;
}
/**
* 从指定方法列表中筛选所有方法上带有指定元数据方法的方法
*
* @param methods 方法列表
* @param lookup 查找器
* @return 方法集合
*/
public static Set<Method> findFromSpecificMethods(Method[] methods, MethodMetadataLookup<?> lookup) {
return findWithMetadataFromSpecificMethods(methods, lookup).keySet();
}
/**
* 从指定方法列表中筛选所有方法上带有指定元数据方法的方法与对应元数据
*
* @param methods 方法列表
* @param lookup 查找器
* @return 方法与对应的元数据
*/
public static <T> Map.Entry<Method, T> getWithMetadataFromSpecificMethods(Method[] methods, MethodMetadataLookup<T> lookup) {
for (Method method : methods) {
T result = lookup.inspect(method);
if (Objects.nonNull(result)) {
return MapUtil.entry(method, result);
}
}
return null;
}
/**
* 从指定方法列表中筛选所有方法上带有指定元数据方法的方法
*
* @param methods 方法列表
* @param lookup 查找器
* @return 方法
*/
public static Method getFromSpecificMethods(Method[] methods, MethodMetadataLookup<?> lookup) {
Map.Entry<Method, ?> result = getWithMetadataFromSpecificMethods(methods, lookup);
return Objects.isNull(result) ? null : result.getKey();
}
// endregion
// region ============= methods =============
/**
* 获取方法上带有指定元数据的方法与对应元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法与对应的元数据集合
*/
public static <T> Map<Method, T> findWithMetadataFromMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
return findWithMetadataFromSpecificMethods(getMethods(type), lookup);
}
/**
* 获取方法上带有指定元数据的方法
*
* @param type 类型
* @param lookup 查找器
* @return 方法集合
*/
public static Set<Method> findFromMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
return findFromSpecificMethods(getMethods(type), lookup);
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法及元数据若无任何匹配的结果则返回{@code null}
*/
public static <T> Map.Entry<Method, T> getWithMetadataFromMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
return getWithMetadataFromSpecificMethods(getMethods(type), lookup);
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法若无任何匹配的结果则返回{@code null}
*/
public static Method getFromMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
return getFromSpecificMethods(getMethods(type), lookup);
}
// endregion
// region ============= declared methods =============
/**
* 获取方法上带有指定元数据的方法与对应元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法与对应的元数据集合
*/
public static <T> Map<Method, T> findWithMetadataFromDeclaredMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
return findWithMetadataFromSpecificMethods(getDeclaredMethods(type), lookup);
}
/**
* 获取方法上带有指定元数据的方法
*
* @param type 类型
* @param lookup 查找器
* @return 方法集合
*/
public static Set<Method> findFromDeclaredMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
return findFromSpecificMethods(getDeclaredMethods(type), lookup);
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法及元数据若无任何匹配的结果则返回{@code null}
*/
public static <T> Map.Entry<Method, T> getWithMetadataFromDeclaredMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
return getWithMetadataFromSpecificMethods(getDeclaredMethods(type), lookup);
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法若无任何匹配的结果则返回{@code null}
*/
public static Method getFromDeclaredMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
return getFromSpecificMethods(getDeclaredMethods(type), lookup);
}
// endregion
// region ============= all methods =============
/**
* 获取方法上带有指定元数据的方法与对应元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法与对应的元数据集合
*/
public static <T> Map<Method, T> findWithMetadataFromAllMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
return findWithMetadataFromSpecificMethods(getAllMethods(type), lookup);
}
/**
* 获取方法上带有指定元数据的方法
*
* @param type 类型
* @param lookup 查找器
* @return 方法集合
*/
public static Set<Method> findFromAllMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
return findFromSpecificMethods(getAllMethods(type), lookup);
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法及元数据若无任何匹配的结果则返回{@code null}
*/
public static <T> Map.Entry<Method, T> getWithMetadataFromAllMethods(Class<?> type, MethodMetadataLookup<T> lookup) {
if (Objects.isNull(type)) {
return null;
}
Mutable<Map.Entry<Method, T>> result = new MutableObj<>();
ClassUtil.traverseTypeHierarchyWhile(type, t -> {
Map.Entry<Method, T> target = getWithMetadataFromDeclaredMethods(t, lookup);
if (Objects.nonNull(target)) {
result.set(target);
return false;
}
return true;
});
return result.get();
}
/**
* 获取首个方法上带有指定元数据的方法及元数据
*
* @param type 类型
* @param lookup 查找器
* @return 方法若无任何匹配的结果则返回{@code null}
*/
public static Method getFromAllMethods(Class<?> type, MethodMetadataLookup<?> lookup) {
Map.Entry<Method, ?> target = getWithMetadataFromAllMethods(type, lookup);
return Objects.isNull(target) ? null : target.getKey();
}
// endregion
}

View File

@ -0,0 +1,63 @@
package org.dromara.hutool.core.reflect.method;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
/**
* test for {@link MethodMatcher}
*
* @author huangchengxing
*/
class MethodMatcherTest {
private MethodMatcher matchToString = (MethodMatcher)t -> "toString".equals(t.getName());
@SneakyThrows
@Test
void test() {
Method toString = Object.class.getDeclaredMethod("toString");
Assertions.assertTrue(matchToString.test(toString));
Method hashCode = Object.class.getDeclaredMethod("hashCode");
Assertions.assertFalse(matchToString.test(hashCode));
}
@SneakyThrows
@Test
void and() {
Method toString = Object.class.getDeclaredMethod("toString");
Assertions.assertTrue(matchToString.test(toString));
MethodMatcher newMatcher = matchToString.and(t -> t.getReturnType() == String.class);
Assertions.assertTrue(newMatcher.test(toString));
}
@SneakyThrows
@Test
void negate() {
Method toString = Object.class.getDeclaredMethod("toString");
Assertions.assertTrue(matchToString.test(toString));
MethodMatcher newMatcher = matchToString.negate();
Assertions.assertFalse(newMatcher.test(toString));
}
@SneakyThrows
@Test
void or() {
MethodMatcher newMatcher = matchToString.or(t -> "hashCode".equals(t.getName()));
Method toString = Object.class.getDeclaredMethod("toString");
Method hashCode = Object.class.getDeclaredMethod("hashCode");
Assertions.assertTrue(newMatcher.test(toString));
Assertions.assertTrue(newMatcher.test(hashCode));
}
@SneakyThrows
@Test
void inspect() {
Method toString = Object.class.getDeclaredMethod("toString");
Assertions.assertTrue(matchToString.inspect(toString));
Method hashCode = Object.class.getDeclaredMethod("hashCode");
Assertions.assertNull(matchToString.inspect(hashCode));
}
}

View File

@ -0,0 +1,662 @@
package org.dromara.hutool.core.reflect.method;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* test for {@link MethodMatcherUtils}
*
* @author huangchengxing
*/
class MethodMatcherUtilsTest {
private Method noneReturnNoArgs;
private Method noneReturnOneArgs;
private Method noneReturnTwoArgs;
private Method noneReturnTwoArgs2;
private Method returnNoArgs;
private Method returnOneArgs;
private Method returnTwoArgs;
@SneakyThrows
@BeforeEach
void init() {
this.noneReturnNoArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("noneReturnNoArgs");
this.noneReturnOneArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("noneReturnOneArgs", String.class);
this.noneReturnTwoArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("noneReturnTwoArgs", String.class, List.class);
this.noneReturnTwoArgs2 = MethodMatcherUtilsTest.class.getDeclaredMethod("noneReturnTwoArgs", CharSequence.class, Collection.class);
this.returnNoArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("returnNoArgs");
this.returnOneArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("returnOneArgs", String.class);
this.returnTwoArgs = MethodMatcherUtilsTest.class.getDeclaredMethod("returnTwoArgs", String.class, List.class);
}
@Test
void testForName() {
MethodMatcher methodMatcher = MethodMatcherUtils.forName("noneReturnNoArgs");
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
}
@Test
void forNameIgnoreCase() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNameIgnoreCase("noneReturnNoArgs");
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
// if name is upper case, it will be ignored
methodMatcher = MethodMatcherUtils.forNameIgnoreCase("NONERETURNNOARGS");
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
}
@Test
void forNoneReturnType() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNoneReturnType();
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void forReturnType() {
MethodMatcher methodMatcher = MethodMatcherUtils.forReturnType(Collection.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertTrue(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forStrictReturnType() {
MethodMatcher methodMatcher = MethodMatcherUtils.forStrictReturnType(Collection.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
// only match return type is strict equal to parameter type
methodMatcher = MethodMatcherUtils.forStrictReturnType(List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forParameterCount() {
MethodMatcher methodMatcher = MethodMatcherUtils.forParameterCount(2);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
}
@Test
void forMostSpecificParameterTypes() {
// match none args method
MethodMatcher methodMatcher = MethodMatcherUtils.forMostSpecificParameterTypes();
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
// match all args types
methodMatcher = MethodMatcherUtils.forMostSpecificParameterTypes(null, null);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
// match first arg type
methodMatcher = MethodMatcherUtils.forMostSpecificParameterTypes(CharSequence.class, null);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
// match second arg type
methodMatcher = MethodMatcherUtils.forMostSpecificParameterTypes(null, Collection.class);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
// match two arg type
methodMatcher = MethodMatcherUtils.forMostSpecificParameterTypes(CharSequence.class, Collection.class);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
}
@Test
void forMostSpecificStrictParameterTypes() {
// match none args method
MethodMatcher methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes();
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
// match all args types
methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes(null, null);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
// match first arg type
methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes(CharSequence.class, null);
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes(String.class, null);
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
// match second arg type
methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes(null, Collection.class);
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
// match two arg type
methodMatcher = MethodMatcherUtils.forMostSpecificStrictParameterTypes(CharSequence.class, Collection.class);
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
}
@Test
void forParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forParameterTypes();
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
// match parameter types is empty
methodMatcher = MethodMatcherUtils.forParameterTypes(CharSequence.class, Collection.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forStrictParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forStrictParameterTypes();
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
// cannot match assignable parameter types
methodMatcher = MethodMatcherUtils.forStrictParameterTypes(CharSequence.class, Collection.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
// only match parameter types is strict equal to parameter type
methodMatcher = MethodMatcherUtils.forStrictParameterTypes(String.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void noneMatch() {
MethodMatcher methodMatcher = MethodMatcherUtils.noneMatch();
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertTrue(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
// combine with other matchers
methodMatcher = MethodMatcherUtils.noneMatch(
MethodMatcherUtils.forName("noneReturnNoArgs"),
MethodMatcherUtils.forReturnType(Collection.class)
);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnOneArgs));
}
@Test
void anyMatch() {
MethodMatcher methodMatcher = MethodMatcherUtils.anyMatch();
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
// combine with other matchers
methodMatcher = MethodMatcherUtils.anyMatch(
MethodMatcherUtils.forName("noneReturnNoArgs"),
MethodMatcherUtils.forReturnType(Collection.class)
);
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void allMatch() {
MethodMatcher methodMatcher = MethodMatcherUtils.allMatch();
Assertions.assertTrue(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertTrue(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
// combine with other matchers
methodMatcher = MethodMatcherUtils.allMatch(
MethodMatcherUtils.forName("noneReturnNoArgs"),
MethodMatcherUtils.forReturnType(Collection.class)
);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void isPublic() {
MethodMatcher methodMatcher = MethodMatcherUtils.isPublic();
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertTrue(methodMatcher.test(returnNoArgs));
Assertions.assertTrue(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void isStatic() {
MethodMatcher methodMatcher = MethodMatcherUtils.isStatic();
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void isPublicStatic() {
MethodMatcher methodMatcher = MethodMatcherUtils.isPublicStatic();
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void forModifiers() {
MethodMatcher methodMatcher = MethodMatcherUtils.forModifiers(Modifier.PUBLIC, Modifier.STATIC);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void forNameAndParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNameAndParameterTypes("noneReturnTwoArgs", CharSequence.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void forNameAndStrictParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNameAndStrictParameterTypes("noneReturnTwoArgs", CharSequence.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
methodMatcher = MethodMatcherUtils.forNameAndStrictParameterTypes("returnTwoArgs", String.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forNameIgnoreCaseAndParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNameIgnoreCaseAndParameterTypes("NONEReturnTWOArgs", CharSequence.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
methodMatcher = MethodMatcherUtils.forNameIgnoreCaseAndParameterTypes("ReturnTWOArgs", String.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forNameIgnoreCaseAndStrictParameterTypes() {
MethodMatcher methodMatcher = MethodMatcherUtils.forNameIgnoreCaseAndStrictParameterTypes("NONEReturnTWOArgs", CharSequence.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
methodMatcher = MethodMatcherUtils.forNameIgnoreCaseAndStrictParameterTypes("ReturnTWOArgs", String.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forStrictMethodSignature() {
MethodMatcher methodMatcher = MethodMatcherUtils.forStrictMethodSignature("noneReturnTwoArgs", null, CharSequence.class, Collection.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs2));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
methodMatcher = MethodMatcherUtils.forStrictMethodSignature("noneReturnTwoArgs", null, String.class, List.class);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs2));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
}
@Test
void forStrictMethodSignatureWithMethod() {
MethodMatcher methodMatcher = MethodMatcherUtils.forStrictMethodSignature(noneReturnTwoArgs);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
methodMatcher = MethodMatcherUtils.forStrictMethodSignature(returnTwoArgs);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertTrue(methodMatcher.test(returnTwoArgs));
}
@Test
void forMethodSignatureWithMethod() {
MethodMatcher methodMatcher = MethodMatcherUtils.forMethodSignature(noneReturnTwoArgs2);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs2));
}
@Test
void forMethodSignature() {
MethodMatcher methodMatcher = MethodMatcherUtils.forMethodSignature(
"noneReturnTwoArgs", null, CharSequence.class, Collection.class
);
Assertions.assertFalse(methodMatcher.test(noneReturnNoArgs));
Assertions.assertFalse(methodMatcher.test(noneReturnOneArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs));
Assertions.assertFalse(methodMatcher.test(returnNoArgs));
Assertions.assertFalse(methodMatcher.test(returnOneArgs));
Assertions.assertFalse(methodMatcher.test(returnTwoArgs));
Assertions.assertTrue(methodMatcher.test(noneReturnTwoArgs2));
}
@Test
@SneakyThrows
void forGetterMethodWithField() {
Field nameField = Foo.class.getDeclaredField("name");
MethodMatcher methodMatcher = MethodMatcherUtils.forGetterMethod(nameField);
Method getName = Foo.class.getMethod("getName");
Assertions.assertTrue(methodMatcher.test(getName));
Field flagField = Foo.class.getDeclaredField("flag");
methodMatcher = MethodMatcherUtils.forGetterMethod(flagField);
Method isFlag = Foo.class.getMethod("isFlag");
Assertions.assertTrue(methodMatcher.test(isFlag));
Field objectField = Foo.class.getDeclaredField("object");
methodMatcher = MethodMatcherUtils.forGetterMethod(objectField);
Method object = Foo.class.getMethod("object");
Assertions.assertTrue(methodMatcher.test(object));
}
@Test
@SneakyThrows
void forGetterMethod() {
MethodMatcher methodMatcher = MethodMatcherUtils.forGetterMethod("name", String.class);
Method getName = Foo.class.getMethod("getName");
Assertions.assertTrue(methodMatcher.test(getName));
methodMatcher = MethodMatcherUtils.forGetterMethod("flag", boolean.class);
Method isFlag = Foo.class.getMethod("isFlag");
Assertions.assertTrue(methodMatcher.test(isFlag));
methodMatcher = MethodMatcherUtils.forGetterMethod("object", Object.class);
Method object = Foo.class.getMethod("object");
Assertions.assertTrue(methodMatcher.test(object));
}
@Test
@SneakyThrows
void forSetterMethodWithField() {
Field nameField = Foo.class.getDeclaredField("name");
MethodMatcher methodMatcher = MethodMatcherUtils.forSetterMethod(nameField);
Method setName = Foo.class.getMethod("setName", String.class);
Assertions.assertTrue(methodMatcher.test(setName));
Field flagField = Foo.class.getDeclaredField("flag");
methodMatcher = MethodMatcherUtils.forSetterMethod(flagField);
Method setFlag = Foo.class.getMethod("setFlag", boolean.class);
Assertions.assertTrue(methodMatcher.test(setFlag));
Field objectField = Foo.class.getDeclaredField("object");
methodMatcher = MethodMatcherUtils.forSetterMethod(objectField);
Method object = Foo.class.getMethod("object", Object.class);
Assertions.assertTrue(methodMatcher.test(object));
}
@Test
@SneakyThrows
void forSetterMethod() {
MethodMatcher methodMatcher = MethodMatcherUtils.forSetterMethod("name", String.class);
Method setName = Foo.class.getMethod("setName", String.class);
Assertions.assertTrue(methodMatcher.test(setName));
methodMatcher = MethodMatcherUtils.forSetterMethod("flag", boolean.class);
Method setFlag = Foo.class.getMethod("setFlag", boolean.class);
Assertions.assertTrue(methodMatcher.test(setFlag));
methodMatcher = MethodMatcherUtils.forSetterMethod("object", Object.class);
Method object = Foo.class.getMethod("object", Object.class);
Assertions.assertTrue(methodMatcher.test(object));
}
@Test
@SneakyThrows
void hasDeclaredAnnotation() {
MethodMatcher methodMatcher = MethodMatcherUtils.hasDeclaredAnnotation(GrandParentAnnotation.class);
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
}
@Test
@SneakyThrows
void hasAnnotation() {
MethodMatcher methodMatcher = MethodMatcherUtils.hasAnnotation(GrandParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
methodMatcher = MethodMatcherUtils.hasAnnotation(ParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
}
@Test
@SneakyThrows
void hasAnnotationOnDeclaringClass() {
MethodMatcher methodMatcher = MethodMatcherUtils.hasAnnotationOnDeclaringClass(GrandParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
methodMatcher = MethodMatcherUtils.hasAnnotationOnDeclaringClass(ParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
methodMatcher = MethodMatcherUtils.hasAnnotationOnDeclaringClass(ChildAnnotation.class);
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
}
@Test
@SneakyThrows
void hasAnnotationOnMethodOrDeclaringClass() {
MethodMatcher methodMatcher = MethodMatcherUtils.hasAnnotationOnMethodOrDeclaringClass(GrandParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
methodMatcher = MethodMatcherUtils.hasAnnotationOnMethodOrDeclaringClass(ParentAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
methodMatcher = MethodMatcherUtils.hasAnnotationOnMethodOrDeclaringClass(ChildAnnotation.class);
Assertions.assertTrue(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByChildAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("annotatedByGrandParentAnnotation")));
Assertions.assertFalse(methodMatcher.test(AnnotatedClass.class.getDeclaredMethod("noneAnnotated")));
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
private @interface GrandParentAnnotation {}
@GrandParentAnnotation
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
private @interface ParentAnnotation {}
@ParentAnnotation
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
private @interface ChildAnnotation {}
@ParentAnnotation
private static class AnnotatedClass {
@ChildAnnotation
private void annotatedByChildAnnotation() { }
@ParentAnnotation
private static void annotatedByParentAnnotation() { }
@GrandParentAnnotation
public static void annotatedByGrandParentAnnotation() { }
public static void noneAnnotated() { }
}
private static class Foo {
@Setter
@Getter
private String name;
@Setter
@Getter
private boolean flag;
private Object object;
public void setName(String name, Void none) { }
public Object object() {
return object;
}
public Foo object(Object object) {
this.object = object;
return this;
}
}
private void noneReturnNoArgs() { }
private static void noneReturnOneArgs(String arg1) { }
public static void noneReturnTwoArgs(String arg1, List<String> stringList) { }
public static void noneReturnTwoArgs(CharSequence arg1, Collection<String> stringList) { }
public List<String> returnNoArgs() { return null; }
public Set<String> returnOneArgs(String arg1) { return null; }
public List<String> returnTwoArgs(String arg1, List<String> stringList) { return null; }
}

View File

@ -0,0 +1,279 @@
package org.dromara.hutool.core.reflect.method;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/**
* test for {@link MethodScanner}
*
* @author huangchengxing
*/
class MethodScannerTest {
@Test
void testGetMethods() {
Assertions.assertEquals(0, MethodScanner.getMethods(null).length);
Method[] actual = MethodScanner.getMethods(Child.class);
Assertions.assertSame(actual, MethodScanner.getMethods(Child.class));
Method[] expected = Child.class.getMethods();
Assertions.assertArrayEquals(expected, actual);
}
@Test
void testGetDeclaredMethods() {
Assertions.assertEquals(0, MethodScanner.getDeclaredMethods(null).length);
Method[] actual = MethodScanner.getDeclaredMethods(Child.class);
Assertions.assertSame(actual, MethodScanner.getDeclaredMethods(Child.class));
Method[] expected = Child.class.getDeclaredMethods();
Assertions.assertArrayEquals(expected, actual);
}
@Test
void testGetAllMethods() {
Assertions.assertEquals(0, MethodScanner.getAllMethods(null).length);
Method[] actual = MethodScanner.getAllMethods(Child.class);
// get declared method from childparentgrandparent
Method[] expected = Stream.of(Child.class, Parent.class, Interface.class, Object.class)
.flatMap(c -> Stream.of(MethodScanner.getDeclaredMethods(c)))
.toArray(Method[]::new);
Assertions.assertArrayEquals(expected, actual);
}
@Test
void testClearCaches() {
Method[] declaredMethods = MethodScanner.getDeclaredMethods(Child.class);
Assertions.assertSame(declaredMethods, MethodScanner.getDeclaredMethods(Child.class));
Method[] methods = MethodScanner.getMethods(Child.class);
Assertions.assertSame(methods, MethodScanner.getMethods(Child.class));
// clear method cache
MethodScanner.clearCaches();
Assertions.assertNotSame(declaredMethods, MethodScanner.getDeclaredMethods(Child.class));
Assertions.assertNotSame(methods, MethodScanner.getMethods(Child.class));
}
@SneakyThrows
@Test
void testFindWithMetadataFromSpecificMethods() {
Assertions.assertTrue(MethodScanner.findWithMetadataFromSpecificMethods(null, m -> m.getAnnotation(Annotation.class)).isEmpty());
Method[] methods = MethodScanner.getMethods(Child.class);
Map<Method, Annotation> actual = MethodScanner.findWithMetadataFromSpecificMethods(methods, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.containsKey(expectedMethod));
// check annotation
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.get(expectedMethod));
}
@SneakyThrows
@Test
void testFindFromSpecificMethods() {
Method[] methods = MethodScanner.getMethods(Child.class);
Set<Method> actual = MethodScanner.findFromSpecificMethods(methods, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.contains(expectedMethod));
}
@SneakyThrows
@Test
void testGetWithMetadataFromSpecificMethods() {
// find first oneArgMethod method
Method[] methods = MethodScanner.getMethods(Child.class);
Map.Entry<Method, Boolean> actual = MethodScanner.getWithMetadataFromSpecificMethods(methods, MethodMatcherUtils.forName("oneArgMethod"));
Assertions.assertNotNull(actual);
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual.getKey());
Assertions.assertTrue(actual.getValue());
}
@SneakyThrows
@Test
void testGetFromSpecificMethods() {
// find first oneArgMethod method
Method[] methods = MethodScanner.getMethods(Child.class);
Method actual = MethodScanner.getFromSpecificMethods(methods, MethodMatcherUtils.forName("oneArgMethod"));
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual);
}
@SneakyThrows
@Test
void testFindWithMetadataFromMethods() {
Map<Method, Annotation> actual = MethodScanner.findWithMetadataFromMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.containsKey(expectedMethod));
// check annotation
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.get(expectedMethod));
}
@SneakyThrows
@Test
void testFindFromMethods() {
Set<Method> actual = MethodScanner.findFromMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.contains(expectedMethod));
}
@SneakyThrows
@Test
void testGetWithMetadataFromMethods() {
Map.Entry<Method, Annotation> actual = MethodScanner.getWithMetadataFromMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertNotNull(actual);
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual.getKey());
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.getValue());
}
@SneakyThrows
@Test
void testGetFromMethods() {
Method actual = MethodScanner.getFromMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual);
}
@SneakyThrows
@Test
void testFindWithMetadataFromDeclaredMethods() {
Map<Method, Annotation> actual = MethodScanner.findWithMetadataFromDeclaredMethods(Parent.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.containsKey(expectedMethod));
// check annotation
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.get(expectedMethod));
}
@SneakyThrows
@Test
void testFindFromDeclaredMethods() {
Set<Method> actual = MethodScanner.findFromDeclaredMethods(Parent.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.contains(expectedMethod));
}
@SneakyThrows
@Test
void testGetWithMetadataFromDeclaredMethods() {
Map.Entry<Method, Annotation> actual = MethodScanner.getWithMetadataFromDeclaredMethods(Parent.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertNotNull(actual);
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual.getKey());
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.getValue());
}
@SneakyThrows
@Test
void testGetFromDeclaredMethods() {
Method actual = MethodScanner.getFromDeclaredMethods(Parent.class, m -> m.getAnnotation(Annotation.class));
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual);
}
@SneakyThrows
@Test
void testFindWithMetadataFromAllMethods() {
Map<Method, Annotation> actual = MethodScanner.findWithMetadataFromAllMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.containsKey(expectedMethod));
// check annotation
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.get(expectedMethod));
}
@SneakyThrows
@Test
void testFindFromAllMethods() {
Set<Method> actual = MethodScanner.findFromAllMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertEquals(1, actual.size());
// check method
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertTrue(actual.contains(expectedMethod));
}
@SneakyThrows
@Test
void testGetWithMetadataFromAllMethods() {
Assertions.assertNull(MethodScanner.getWithMetadataFromAllMethods(Child.class, m -> m.getAnnotation(Deprecated.class)));
Map.Entry<Method, Annotation> actual = MethodScanner.getWithMetadataFromAllMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Assertions.assertNotNull(actual);
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual.getKey());
Annotation expectedAnnotation = expectedMethod.getAnnotation(Annotation.class);
Assertions.assertEquals(expectedAnnotation, actual.getValue());
}
@SneakyThrows
@Test
void testGetFromAllMethods() {
Method actual = MethodScanner.getFromAllMethods(Child.class, m -> m.getAnnotation(Annotation.class));
Method expectedMethod = Parent.class.getDeclaredMethod("oneArgMethod", String.class);
Assertions.assertEquals(expectedMethod, actual);
}
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation {
String value() default "default";
}
@Annotation("Interface")
public interface Interface {
static void staticMethod() {
}
void noneArgMethod();
void oneArgMethod(String arg);
}
public static class Parent implements Interface {
@Override
public void noneArgMethod() { }
@Annotation("oneArgMethod")
@Override
public void oneArgMethod(String arg) { }
}
public static class Child extends Parent implements Interface {
}
}