Commit 45cda665 authored by ma yanling's avatar ma yanling
Browse files

project commit

parent ad2fb30a
Pipeline #2354 failed with stages
in 0 seconds
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* <p>注解扫描器,用于从支持的可注解元素上获取所需注解
*
* <p>默认提供了以下扫描方式:
* <ul>
* <li>{@link #NOTHING}:什么都不做,什么注解都不扫描;</li>
* <li>{@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;</li>
* <li>
* {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
* 以及这些注解的元注解;
* </li>
* <li>{@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;</li>
* <li>{@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;</li>
* <li>{@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;</li>
* <li>{@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
* <li>{@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;</li>
* <li>{@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
* </ul>
*
* @author huangchengxing
* @see TypeAnnotationScanner
* @see MethodAnnotationScanner
* @see FieldAnnotationScanner
* @see MetaAnnotationScanner
* @see ElementAnnotationScanner
* @see GenericAnnotationScanner
*/
public interface AnnotationScanner {
// ============================ 预置的扫描器实例 ============================
/**
* 不扫描任何注解
*/
AnnotationScanner NOTHING = new EmptyAnnotationScanner();
/**
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解的扫描器
*/
AnnotationScanner DIRECTLY = new GenericAnnotationScanner(false, false, false);
/**
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner DIRECTLY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, false);
/**
* 扫描元素本身以及父类的层级结构中声明的注解的扫描器
*/
AnnotationScanner SUPERCLASS = new GenericAnnotationScanner(false, true, false);
/**
* 扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner SUPERCLASS_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, false);
/**
* 扫描元素本身以及父接口的层级结构中声明的注解的扫描器
*/
AnnotationScanner INTERFACE = new GenericAnnotationScanner(false, false, true);
/**
* 扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner INTERFACE_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, true);
/**
* 扫描元素本身以及父类、父接口的层级结构中声明的注解的扫描器
*/
AnnotationScanner TYPE_HIERARCHY = new GenericAnnotationScanner(false, true, true);
/**
* 扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner TYPE_HIERARCHY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, true);
// ============================ 静态方法 ============================
/**
* 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.filter(scanner -> scanner.support(annotatedEle))
.findFirst()
.map(scanner -> scanner.getAnnotations(annotatedEle))
.orElseGet(Collections::emptyList);
}
/**
* 根据指定的扫描器,扫描元素上可能存在的注解
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAllSupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.map(scanner -> scanner.getAnnotationsIfSupport(annotatedEle))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
// ============================ 抽象方法 ============================
/**
* 判断是否支持扫描该注解元素
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
default boolean support(AnnotatedElement annotatedEle) {
return false;
}
/**
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
default List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
final List<Annotation> annotations = new ArrayList<>();
scan((index, annotation) -> annotations.add(annotation), annotatedEle, null);
return annotations;
}
/**
* 若{@link #support(AnnotatedElement)}返回{@code true},
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果,
* 否则返回{@link Collections#emptyList()}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
default List<Annotation> getAnnotationsIfSupport(AnnotatedElement annotatedEle) {
return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList();
}
/**
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理。
* 调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
default void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, (a)->annotation -> true);
for (final Annotation annotation : annotatedEle.getAnnotations()) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
consumer.accept(0, annotation);
}
}
}
/**
* 若{@link #support(AnnotatedElement)}返回{@code true},则调用{@link #scan(BiConsumer, AnnotatedElement, Predicate)}
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
default void scanIfSupport(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
if (support(annotatedEle)) {
scan(consumer, annotatedEle, filter);
}
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* 扫描{@link AnnotatedElement}上的注解,不支持处理层级对象
*
* @author huangchengxing
*/
public class ElementAnnotationScanner implements AnnotationScanner {
/**
* 判断是否支持扫描该注解元素,仅当注解元素不为空时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return ObjectUtil.isNotNull(annotatedEle);
}
/**
* 扫描{@link AnnotatedElement}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter,a-> t -> true);
Stream.of(annotatedEle.getAnnotations())
.filter(filter)
.forEach(annotation -> consumer.accept(0, annotation));
}
}
package cn.hutool.core.annotation.scanner;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* 默认不扫描任何元素的扫描器
*
* @author huangchengxing
*/
public class EmptyAnnotationScanner implements AnnotationScanner {
@Override
public boolean support(AnnotatedElement annotatedEle) {
return true;
}
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
return Collections.emptyList();
}
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
// do nothing
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* 扫描{@link Field}上的注解
*
* @author huangchengxing
*/
public class FieldAnnotationScanner implements AnnotationScanner {
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Field}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Field;
}
/**
* 扫描{@link Field}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
for (final Annotation annotation : annotatedEle.getAnnotations()) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
consumer.accept(0, annotation);
}
}
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* <p>通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
*
* <p>当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
* <ul>
* <li>
* 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
* 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
* </li>
* <li>
* 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
* 扫描器将扫描层级结构中类、接口声明的注解;
* </li>
* <li>当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;</li>
* </ul>
* 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
*
* @author huangchengxing
* @see TypeAnnotationScanner
* @see MethodAnnotationScanner
* @see MetaAnnotationScanner
* @see ElementAnnotationScanner
*/
public class GenericAnnotationScanner implements AnnotationScanner {
/**
* 类型扫描器
*/
private final AnnotationScanner typeScanner;
/**
* 方法扫描器
*/
private final AnnotationScanner methodScanner;
/**
* 元注解扫描器
*/
private final AnnotationScanner metaScanner;
/**
* 普通元素扫描器
*/
private final AnnotationScanner elementScanner;
/**
* 通用注解扫描器支持扫描所有类型的{@link AnnotatedElement}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return true;
}
/**
* 构造一个通用注解扫描器
*
* @param enableScanMetaAnnotation 是否扫描注解上的元注解
* @param enableScanSupperClass 是否扫描父类
* @param enableScanSupperInterface 是否扫描父接口
*/
public GenericAnnotationScanner(
boolean enableScanMetaAnnotation,
boolean enableScanSupperClass,
boolean enableScanSupperInterface) {
this.metaScanner = enableScanMetaAnnotation ? new MetaAnnotationScanner() : new EmptyAnnotationScanner();
this.typeScanner = new TypeAnnotationScanner(
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
);
this.methodScanner = new MethodAnnotationScanner(
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
);
this.elementScanner = new ElementAnnotationScanner();
}
/**
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
if (ObjectUtil.isNull(annotatedEle)) {
return;
}
// 注解元素是类
if (annotatedEle instanceof Class) {
scanElements(typeScanner, consumer, annotatedEle, filter);
}
// 注解元素是方法
else if (annotatedEle instanceof Method) {
scanElements(methodScanner, consumer, annotatedEle, filter);
}
// 注解元素是其他类型
else {
scanElements(elementScanner, consumer, annotatedEle, filter);
}
}
/**
* 扫描注解类的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
*
* @param scanner 使用的扫描器
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
private void scanElements(
AnnotationScanner scanner,
BiConsumer<Integer, Annotation> consumer,
AnnotatedElement annotatedEle,
Predicate<Annotation> filter) {
// 扫描类上注解
final ListValueMap<Integer, Annotation> classAnnotations = new ListValueMap<>(new LinkedHashMap<>());
scanner.scan((index, annotation) -> {
if (filter.test(annotation)) {
classAnnotations.putValue(index, annotation);
}
}, annotatedEle, filter);
// 扫描元注解
classAnnotations.forEach((index, annotations) ->
annotations.forEach(annotation -> {
consumer.accept(index, annotation);
metaScanner.scan(consumer, annotation.annotationType(), filter);
})
);
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 扫描注解类上存在的注解,支持处理枚举实例或枚举类型
* 需要注意,当待解析是枚举类时,有可能与{@link TypeAnnotationScanner}冲突
*
* @author huangchengxing
* @see TypeAnnotationScanner
*/
public class MetaAnnotationScanner implements AnnotationScanner {
/**
* 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
*/
private final boolean includeSupperMetaAnnotation;
/**
* 构造一个元注解扫描器
*
* @param includeSupperMetaAnnotation 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
*/
public MetaAnnotationScanner(boolean includeSupperMetaAnnotation) {
this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
}
/**
* 构造一个元注解扫描器,默认在扫描当前注解上的元注解后,并继续递归扫描元注解
*/
public MetaAnnotationScanner() {
this(true);
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Annotation}接口的子类{@link Class}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return (annotatedEle instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>) annotatedEle));
}
/**
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
final List<Annotation> annotations = new ArrayList<>();
scan(
(index, annotation) -> annotations.add(annotation), annotatedEle,
annotation -> ObjectUtil.notEqual(annotation, annotatedEle)
);
return annotations;
}
/**
* 按广度优先扫描指定注解上的元注解,对扫描到的注解与层级索引进行操作
*
* @param consumer 当前层级索引与操作
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 过滤器
*/
@SuppressWarnings("unchecked")
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
Set<Class<? extends Annotation>> accessed = new HashSet<>();
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class<? extends Annotation>) annotatedEle));
int distance = 0;
do {
final List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
for (final Class<? extends Annotation> type : annotationTypes) {
final List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.filter(filter)
.collect(Collectors.toList());
for (final Annotation metaAnnotation : metaAnnotations) {
consumer.accept(distance, metaAnnotation);
}
accessed.add(type);
List<Class<? extends Annotation>> next = metaAnnotations.stream()
.map(Annotation::annotationType)
.filter(t -> !accessed.contains(t))
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(next)) {
deque.addLast(next);
}
}
distance++;
} while (includeSupperMetaAnnotation && !deque.isEmpty());
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* 扫描{@link Method}上的注解
*
* @author huangchengxing
*/
public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<MethodAnnotationScanner> implements AnnotationScanner {
/**
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
*/
public MethodAnnotationScanner() {
this(false);
}
/**
* 构造一个类注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
*/
public MethodAnnotationScanner(boolean scanSameSignatureMethod) {
this(scanSameSignatureMethod, targetClass -> true, CollUtil.newLinkedHashSet());
}
/**
* 构造一个方法注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
}
/**
* 构造一个方法注解扫描器
*
* @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
* @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
super(includeSuperClass, includeInterfaces, filter, excludeTypes);
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Method}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return boolean 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Method;
}
/**
* 获取声明该方法的类
*
* @param annotatedElement 注解元素
* @return 要递归的类型
* @see Method#getDeclaringClass()
*/
@Override
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement) {
return ((Method)annotatedElement).getDeclaringClass();
}
/**
* 若父类/父接口中方法具有相同的方法签名,则返回该方法上的注解
*
* @param source 原始方法
* @param index 类的层级索引
* @param targetClass 类
* @return 最终所需的目标注解
*/
@Override
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
final Method sourceMethod = (Method) source;
return Stream.of(ClassUtil.getDeclaredMethods(targetClass))
.filter(superMethod -> !superMethod.isBridge())
.filter(superMethod -> hasSameSignature(sourceMethod, superMethod))
.map(AnnotatedElement::getAnnotations)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
}
/**
* 设置是否扫描类层级结构中具有相同方法签名的方法
*
* @param scanSuperMethodIfOverride 是否扫描类层级结构中具有相同方法签名的方法
* @return 当前实例
*/
public MethodAnnotationScanner setScanSameSignatureMethod(boolean scanSuperMethodIfOverride) {
setIncludeInterfaces(scanSuperMethodIfOverride);
setIncludeSuperClass(scanSuperMethodIfOverride);
return this;
}
/**
* 该方法是否具备与扫描的方法相同的方法签名
*/
private boolean hasSameSignature(Method sourceMethod, Method superMethod) {
if (false == StrUtil.equals(sourceMethod.getName(), superMethod.getName())) {
return false;
}
final Class<?>[] sourceParameterTypes = sourceMethod.getParameterTypes();
final Class<?>[] targetParameterTypes = superMethod.getParameterTypes();
if (sourceParameterTypes.length != targetParameterTypes.length) {
return false;
}
if (!ArrayUtil.containsAll(sourceParameterTypes, targetParameterTypes)) {
return false;
}
return ClassUtil.isAssignable(superMethod.getReturnType(), sourceMethod.getReturnType());
}
}
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* 扫描{@link Class}上的注解
*
* @author huangchengxing
*/
public class TypeAnnotationScanner extends AbstractTypeAnnotationScanner<TypeAnnotationScanner> implements AnnotationScanner {
/**
* 构造一个类注解扫描器
*
* @param includeSupperClass 是否允许扫描父类
* @param includeInterfaces 是否允许扫描父接口
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
super(includeSupperClass, includeInterfaces, filter, excludeTypes);
}
/**
* 构建一个类注解扫描器,默认允许扫描指定元素的父类以及父接口
*/
public TypeAnnotationScanner() {
this(true, true, t -> true, CollUtil.newLinkedHashSet());
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Class}接时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Class;
}
/**
* 将注解元素转为{@link Class}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 要递归的类型
*/
@Override
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedEle) {
return (Class<?>)annotatedEle;
}
/**
* 获取{@link Class#getAnnotations()}
*
* @param source 最初的注解元素
* @param index 类的层级索引
* @param targetClass 类
* @return 类上直接声明的注解
*/
@Override
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
return targetClass.getAnnotations();
}
/**
* 是否允许扫描父类
*
* @param includeSuperClass 是否允许扫描父类
* @return 当前实例
*/
@Override
public TypeAnnotationScanner setIncludeSuperClass(boolean includeSuperClass) {
return super.setIncludeSuperClass(includeSuperClass);
}
/**
* 是否允许扫描父接口
*
* @param includeInterfaces 是否允许扫描父类
* @return 当前实例
*/
@Override
public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
return super.setIncludeInterfaces(includeInterfaces);
}
/**
* 若类型为jdk代理类,则尝试转换为原始被代理类
*/
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
@Override
public Class<?> apply(Class<?> sourceClass) {
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
}
}
}
/**
* 注解包扫描封装
*
* @author looly
*
*/
package cn.hutool.core.annotation.scanner;
package cn.hutool.core.bean;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述<br>
* 查找Getter和Setter方法时会:
*
* <pre>
* 1. 忽略字段和方法名的大小写
* 2. Getter查找getXXX、isXXX、getIsXXX
* 3. Setter查找setXXX、setIsXXX
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
* </pre>
*
* @author looly
* @since 3.1.2
*/
public class BeanDesc implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Bean类
*/
private final Class<?> beanClass;
/**
* 属性Map
*/
private final Map<String, PropDesc> propMap = new LinkedHashMap<>();
/**
* 构造
*
* @param beanClass Bean类
*/
public BeanDesc(Class<?> beanClass) {
Assert.notNull(beanClass);
this.beanClass = beanClass;
init();
}
/**
* 获取Bean的全类名
*
* @return Bean的类名
*/
public String getName() {
return this.beanClass.getName();
}
/**
* 获取Bean的简单类名
*
* @return Bean的类名
*/
public String getSimpleName() {
return this.beanClass.getSimpleName();
}
/**
* 获取字段名-字段属性Map
*
* @param ignoreCase 是否忽略大小写,true为忽略,false不忽略
* @return 字段名-字段属性Map
*/
public Map<String, PropDesc> getPropMap(boolean ignoreCase) {
return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap;
}
/**
* 获取字段属性列表
*
* @return {@link PropDesc} 列表
*/
public Collection<PropDesc> getProps() {
return this.propMap.values();
}
/**
* 获取属性,如果不存在返回null
*
* @param fieldName 字段名
* @return {@link PropDesc}
*/
public PropDesc getProp(String fieldName) {
return this.propMap.get(fieldName);
}
/**
* 获得字段名对应的字段对象,如果不存在返回null
*
* @param fieldName 字段名
* @return 字段值
*/
public Field getField(String fieldName) {
final PropDesc desc = this.propMap.get(fieldName);
return null == desc ? null : desc.getField();
}
/**
* 获取Getter方法,如果不存在返回null
*
* @param fieldName 字段名
* @return Getter方法
*/
public Method getGetter(String fieldName) {
final PropDesc desc = this.propMap.get(fieldName);
return null == desc ? null : desc.getGetter();
}
/**
* 获取Setter方法,如果不存在返回null
*
* @param fieldName 字段名
* @return Setter方法
*/
public Method getSetter(String fieldName) {
final PropDesc desc = this.propMap.get(fieldName);
return null == desc ? null : desc.getSetter();
}
// ------------------------------------------------------------------------------------------------------ Private method start
/**
* 初始化<br>
* 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略
*
* @return this
*/
private BeanDesc init() {
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
PropDesc prop;
for (Field field : ReflectUtil.getFields(this.beanClass)) {
// 排除静态属性和对象子类
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
prop = createProp(field, gettersAndSetters);
// 只有不存在时才放入,防止父类属性覆盖子类属性
this.propMap.putIfAbsent(prop.getFieldName(), prop);
}
}
return this;
}
/**
* 根据字段创建属性描述<br>
* 查找Getter和Setter方法时会:
*
* <pre>
* 1. 忽略字段和方法名的大小写
* 2. Getter查找getXXX、isXXX、getIsXXX
* 3. Setter查找setXXX、setIsXXX
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
* </pre>
*
* @param field 字段
* @param methods 类中所有的方法
* @return {@link PropDesc}
* @since 4.0.2
*/
private PropDesc createProp(Field field, Method[] methods) {
final PropDesc prop = findProp(field, methods, false);
// 忽略大小写重新匹配一次
if (null == prop.getter || null == prop.setter) {
final PropDesc propIgnoreCase = findProp(field, methods, true);
if (null == prop.getter) {
prop.getter = propIgnoreCase.getter;
}
if (null == prop.setter) {
prop.setter = propIgnoreCase.setter;
}
}
return prop;
}
/**
* 查找字段对应的Getter和Setter方法
*
* @param field 字段
* @param gettersOrSetters 类中所有的Getter或Setter方法
* @param ignoreCase 是否忽略大小写匹配
* @return PropDesc
*/
private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) {
final String fieldName = field.getName();
final Class<?> fieldType = field.getType();
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
Method getter = null;
Method setter = null;
String methodName;
for (Method method : gettersOrSetters) {
methodName = method.getName();
if (method.getParameterCount() == 0) {
// 无参数,可能为Getter方法
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
// 方法名与字段名匹配,则为Getter方法
getter = method;
}
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
setter = method;
}
}
if (null != getter && null != setter) {
// 如果Getter和Setter方法都找到了,不再继续寻找
break;
}
}
return new PropDesc(field, getter, setter);
}
/**
* 方法是否为Getter方法<br>
* 匹配规则如下(忽略大小写):
*
* <pre>
* 字段名 -》 方法名
* isName -》 isName
* isName -》 isIsName
* isName -》 getIsName
* name -》 isName
* name -》 getName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
*/
private boolean isMatchGetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
final String handledFieldName;
if (ignoreCase) {
// 全部转为小写,忽略大小写比较
methodName = methodName.toLowerCase();
handledFieldName = fieldName.toLowerCase();
fieldName = handledFieldName;
} else {
handledFieldName = StrUtil.upperFirst(fieldName);
}
// 针对Boolean类型特殊检查
if (isBooleanField) {
if (fieldName.startsWith("is")) {
// 字段已经是is开头
if (methodName.equals(fieldName) // isName -》 isName
|| ("get" + handledFieldName).equals(methodName)// isName -》 getIsName
|| ("is" + handledFieldName).equals(methodName)// isName -》 isIsName
) {
return true;
}
} else if (("is" + handledFieldName).equals(methodName)) {
// 字段非is开头, name -》 isName
return true;
}
}
// 包括boolean的任何类型只有一种匹配情况:name -》 getName
return ("get" + handledFieldName).equals(methodName);
}
/**
* 方法是否为Setter方法<br>
* 匹配规则如下(忽略大小写):
*
* <pre>
* 字段名 -》 方法名
* isName -》 setName
* isName -》 setIsName
* name -》 setName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
*/
private boolean isMatchSetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
final String handledFieldName;
if (ignoreCase) {
// 全部转为小写,忽略大小写比较
methodName = methodName.toLowerCase();
handledFieldName = fieldName.toLowerCase();
fieldName = handledFieldName;
} else {
handledFieldName = StrUtil.upperFirst(fieldName);
}
// 非标准Setter方法跳过
if (false == methodName.startsWith("set")) {
return false;
}
// 针对Boolean类型特殊检查
if (isBooleanField && fieldName.startsWith("is")) {
// 字段是is开头
if (("set" + StrUtil.removePrefix(fieldName, "is")).equals(methodName)// isName -》 setName
|| ("set" + handledFieldName).equals(methodName)// isName -》 setIsName
) {
return true;
}
}
// 包括boolean的任何类型只有一种匹配情况:name -》 setName
return ("set" + handledFieldName).equals(methodName);
}
// ------------------------------------------------------------------------------------------------------ Private method end
}
package cn.hutool.core.bean;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.WeakConcurrentMap;
/**
* Bean属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
*/
public enum BeanDescCache {
INSTANCE;
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
/**
* 获得属性名和{@link BeanDesc}Map映射
*
* @param beanClass Bean的类
* @param supplier 对象不存在时创建对象的函数
* @return 属性名和{@link BeanDesc}映射
* @since 5.4.2
*/
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
}
/**
* 清空全局的Bean属性缓存
*
* @since 5.7.21
*/
public void clear() {
this.bdCache.clear();
}
}
package cn.hutool.core.bean;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
/**
* Bean异常
* @author xiaoleilu
*/
public class BeanException extends RuntimeException{
private static final long serialVersionUID = -8096998667745023423L;
public BeanException(Throwable e) {
super(ExceptionUtil.getMessage(e), e);
}
public BeanException(String message) {
super(message);
}
public BeanException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public BeanException(String message, Throwable throwable) {
super(message, throwable);
}
public BeanException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}
package cn.hutool.core.bean;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.ReferenceConcurrentMap;
import cn.hutool.core.map.WeakConcurrentMap;
import java.beans.PropertyDescriptor;
import java.util.Map;
/**
* Bean属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
*/
public enum BeanInfoCache {
INSTANCE;
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
/**
* 获得属性名和{@link PropertyDescriptor}Map映射
*
* @param beanClass Bean的类
* @param ignoreCase 是否忽略大小写
* @return 属性名和{@link PropertyDescriptor}Map映射
*/
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> beanClass, boolean ignoreCase) {
return getCache(ignoreCase).get(beanClass);
}
/**
* 获得属性名和{@link PropertyDescriptor}Map映射
*
* @param beanClass Bean的类
* @param ignoreCase 是否忽略大小写
* @param supplier 缓存对象产生函数
* @return 属性名和{@link PropertyDescriptor}Map映射
* @since 5.4.1
*/
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(
Class<?> beanClass,
boolean ignoreCase,
Func0<Map<String, PropertyDescriptor>> supplier) {
return getCache(ignoreCase).computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
}
/**
* 加入缓存
*
* @param beanClass Bean的类
* @param fieldNamePropertyDescriptorMap 属性名和{@link PropertyDescriptor}Map映射
* @param ignoreCase 是否忽略大小写
*/
public void putPropertyDescriptorMap(Class<?> beanClass, Map<String, PropertyDescriptor> fieldNamePropertyDescriptorMap, boolean ignoreCase) {
getCache(ignoreCase).put(beanClass, fieldNamePropertyDescriptorMap);
}
/**
* 清空缓存
*
* @since 5.7.21
*/
public void clear() {
this.pdCache.clear();
this.ignoreCasePdCache.clear();
}
/**
* 根据是否忽略字段名的大小写,返回不用Cache对象
*
* @param ignoreCase 是否忽略大小写
* @return {@link ReferenceConcurrentMap}
* @since 5.4.1
*/
private ReferenceConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> getCache(boolean ignoreCase) {
return ignoreCase ? ignoreCasePdCache : pdCache;
}
}
package cn.hutool.core.bean;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
import java.util.*;
/**
* Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
* <ol>
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
* </ol>
* <p>
* 表达式栗子:
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
* </pre>
*
* @author Looly
* @since 4.0.6
*/
public class BeanPath implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 表达式边界符号数组
*/
private static final char[] EXP_CHARS = {CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END};
private boolean isStartWith = false;
protected List<String> patternParts;
/**
* 解析Bean路径表达式为Bean模式<br>
* Bean表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
* <ol>
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
* </ol>
* <p>
* 表达式栗子:
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
* </pre>
*
* @param expression 表达式
* @return BeanPath
*/
public static BeanPath create(final String expression) {
return new BeanPath(expression);
}
/**
* 构造
*
* @param expression 表达式
*/
public BeanPath(final String expression) {
init(expression);
}
/**
* 获取表达式解析后的分段列表
*
* @return 表达式分段列表
*/
public List<String> getPatternParts() {
return this.patternParts;
}
/**
* 获取Bean中对应表达式的值
*
* @param bean Bean对象或Map或List等
* @return 值,如果对应值不存在,则返回null
*/
public Object get(final Object bean) {
return get(this.patternParts, bean, false);
}
/**
* 设置表达式指定位置(或filed对应)的值<br>
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
* 注意:
*
* <pre>
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
* </pre>
*
* @param bean Bean、Map或List
* @param value 值
*/
public void set(final Object bean, final Object value) {
set(bean, this.patternParts, lastIsNumber(this.patternParts), value);
}
@Override
public String toString() {
return this.patternParts.toString();
}
//region Private Methods
/**
* 设置表达式指定位置(或filed对应)的值<br>
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
* 注意:
*
* <pre>
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
* </pre>
*
* @param bean Bean、Map或List
* @param patternParts 表达式块列表
* @param nextNumberPart 下一个值是否
* @param value 值
*/
private void set(Object bean, List<String> patternParts, boolean nextNumberPart, Object value) {
Object subBean = this.get(patternParts, bean, true);
if (null == subBean) {
// 当前节点是空,则先创建父节点
final List<String> parentParts = getParentParts(patternParts);
this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>());
//set中有可能做过转换,因此此处重新获取bean
subBean = this.get(patternParts, bean, true);
}
final Object newSubBean = BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
if(newSubBean != subBean){
// 对象变更,重新加入
this.set(bean, getParentParts(patternParts), nextNumberPart, newSubBean);
}
}
/**
* 判断path列表中末尾的标记是否为数字
*
* @param patternParts path列表
* @return 是否为数字
*/
private static boolean lastIsNumber(List<String> patternParts) {
return NumberUtil.isInteger(patternParts.get(patternParts.size() - 1));
}
/**
* 获取父级路径列表
*
* @param patternParts 路径列表
* @return 父级路径列表
*/
private static List<String> getParentParts(List<String> patternParts) {
return patternParts.subList(0, patternParts.size() - 1);
}
/**
* 获取Bean中对应表达式的值
*
* @param patternParts 表达式分段列表
* @param bean Bean对象或Map或List等
* @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read
* @return 值,如果对应值不存在,则返回null
*/
private Object get(final List<String> patternParts, final Object bean, final boolean ignoreLast) {
int length = patternParts.size();
if (ignoreLast) {
length--;
}
Object subBean = bean;
boolean isFirst = true;
String patternPart;
for (int i = 0; i < length; i++) {
patternPart = patternParts.get(i);
subBean = getFieldValue(subBean, patternPart);
if (null == subBean) {
// 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作)
if (isFirst && false == this.isStartWith && BeanUtil.isMatchName(bean, patternPart, true)) {
subBean = bean;
isFirst = false;
} else {
return null;
}
}
}
return subBean;
}
@SuppressWarnings("unchecked")
private static Object getFieldValue(final Object bean, final String expression) {
if (StrUtil.isBlank(expression)) {
return null;
}
if (StrUtil.contains(expression, ':')) {
// [start:end:step] 模式
final List<String> parts = StrUtil.splitTrim(expression, ':');
final int start = Integer.parseInt(parts.get(0));
final int end = Integer.parseInt(parts.get(1));
int step = 1;
if (3 == parts.size()) {
step = Integer.parseInt(parts.get(2));
}
if (bean instanceof Collection) {
return CollUtil.sub((Collection<?>) bean, start, end, step);
} else if (ArrayUtil.isArray(bean)) {
return ArrayUtil.sub(bean, start, end, step);
}
} else if (StrUtil.contains(expression, ',')) {
// [num0,num1,num2...]模式或者['key0','key1']模式
final List<String> keys = StrUtil.splitTrim(expression, ',');
if (bean instanceof Collection) {
return CollUtil.getAny((Collection<?>) bean, Convert.convert(int[].class, keys));
} else if (ArrayUtil.isArray(bean)) {
return ArrayUtil.getAny(bean, Convert.convert(int[].class, keys));
} else {
final String[] unWrappedKeys = new String[keys.size()];
for (int i = 0; i < unWrappedKeys.length; i++) {
unWrappedKeys[i] = StrUtil.unWrap(keys.get(i), '\'');
}
if (bean instanceof Map) {
// 只支持String为key的Map
return MapUtil.getAny((Map<String, ?>) bean, unWrappedKeys);
} else {
final Map<String, Object> map = BeanUtil.beanToMap(bean);
return MapUtil.getAny(map, unWrappedKeys);
}
}
} else {
// 数字或普通字符串
return BeanUtil.getFieldValue(bean, expression);
}
return null;
}
/**
* 初始化
*
* @param expression 表达式
*/
private void init(final String expression) {
final List<String> localPatternParts = new ArrayList<>();
final int length = expression.length();
final StringBuilder builder = new StringBuilder();
char c;
boolean isNumStart = false;// 下标标识符开始
boolean isInWrap = false; //标识是否在引号内
for (int i = 0; i < length; i++) {
c = expression.charAt(i);
if (0 == i && '$' == c) {
// 忽略开头的$符,表示当前对象
isStartWith = true;
continue;
}
if ('\'' == c) {
// 结束
isInWrap = (false == isInWrap);
continue;
}
if (false == isInWrap && ArrayUtil.contains(EXP_CHARS, c)) {
// 处理边界符号
if (CharUtil.BRACKET_END == c) {
// 中括号(数字下标)结束
if (false == isNumStart) {
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find ']' but no '[' !", expression, i));
}
isNumStart = false;
// 中括号结束加入下标
} else {
if (isNumStart) {
// 非结束中括号情况下发现起始中括号报错(中括号未关闭)
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, i));
} else if (CharUtil.BRACKET_START == c) {
// 数字下标开始
isNumStart = true;
}
// 每一个边界符之前的表达式是一个完整的KEY,开始处理KEY
}
if (builder.length() > 0) {
localPatternParts.add(builder.toString());
}
builder.setLength(0);
} else {
// 非边界符号,追加字符
builder.append(c);
}
}
// 末尾边界符检查
if (isNumStart) {
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, length - 1));
} else {
if (builder.length() > 0) {
localPatternParts.add(builder.toString());
}
}
// 不可变List
this.patternParts = ListUtil.unmodifiable(localPatternParts);
}
//endregion
}
package cn.hutool.core.bean;
import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Editor;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import java.beans.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Bean工具类
*
* <p>
* 把一个拥有对属性进行set和get方法的类,我们就可以称之为JavaBean。
* </p>
*
* @author Looly
* @since 3.1.2
*/
public class BeanUtil {
/**
* 判断是否为可读的Bean对象,判定方法是:
*
* <pre>
* 1、是否存在只有无参数的getXXX方法或者isXXX方法
* 2、是否存在public类型的字段
* </pre>
*
* @param clazz 待测试类
* @return 是否为可读的Bean对象
* @see #hasGetter(Class)
* @see #hasPublicField(Class)
*/
public static boolean isReadableBean(Class<?> clazz) {
return hasGetter(clazz) || hasPublicField(clazz);
}
/**
* 判断是否为Bean对象,判定方法是:
*
* <pre>
* 1、是否存在只有一个参数的setXXX方法
* 2、是否存在public类型的字段
* </pre>
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @see #hasSetter(Class)
* @see #hasPublicField(Class)
*/
public static boolean isBean(Class<?> clazz) {
return hasSetter(clazz) || hasPublicField(clazz);
}
/**
* 判断是否有Setter方法<br>
* 判定方法是否存在只有一个参数的setXXX方法
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @since 4.2.2
*/
public static boolean hasSetter(Class<?> clazz) {
if (ClassUtil.isNormalClass(clazz)) {
for (Method method : clazz.getMethods()) {
if (method.getParameterCount() == 1 && method.getName().startsWith("set")) {
// 检测包含标准的setXXX方法即视为标准的JavaBean
return true;
}
}
}
return false;
}
/**
* 判断是否为Bean对象<br>
* 判定方法是否存在只有无参数的getXXX方法或者isXXX方法
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @since 4.2.2
*/
public static boolean hasGetter(Class<?> clazz) {
if (ClassUtil.isNormalClass(clazz)) {
for (Method method : clazz.getMethods()) {
if (method.getParameterCount() == 0) {
final String name = method.getName();
if (name.startsWith("get") || name.startsWith("is")) {
if (false == "getClass".equals(name)) {
return true;
}
}
}
}
}
return false;
}
/**
* 指定类中是否有public类型字段(static字段除外)
*
* @param clazz 待测试类
* @return 是否有public类型字段
* @since 5.1.0
*/
public static boolean hasPublicField(Class<?> clazz) {
if (ClassUtil.isNormalClass(clazz)) {
for (Field field : clazz.getFields()) {
if (ModifierUtil.isPublic(field) && false == ModifierUtil.isStatic(field)) {
//非static的public字段
return true;
}
}
}
return false;
}
/**
* 创建动态Bean
*
* @param bean 普通Bean或Map
* @return {@link DynaBean}
* @since 3.0.7
*/
public static DynaBean createDynaBean(Object bean) {
return new DynaBean(bean);
}
/**
* 查找类型转换器 {@link PropertyEditor}
*
* @param type 需要转换的目标类型
* @return {@link PropertyEditor}
*/
public static PropertyEditor findEditor(Class<?> type) {
return PropertyEditorManager.findEditor(type);
}
/**
* 获取{@link BeanDesc} Bean描述信息
*
* @param clazz Bean类
* @return {@link BeanDesc}
* @since 3.1.2
*/
public static BeanDesc getBeanDesc(Class<?> clazz) {
return BeanDescCache.INSTANCE.getBeanDesc(clazz, () -> new BeanDesc(clazz));
}
/**
* 遍历Bean的属性
*
* @param clazz Bean类
* @param action 每个元素的处理类
* @since 5.4.2
*/
public static void descForEach(Class<?> clazz, Consumer<? super PropDesc> action) {
getBeanDesc(clazz).getProps().forEach(action);
}
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
/**
* 获得Bean字段描述数组
*
* @param clazz Bean类
* @return 字段描述数组
* @throws BeanException 获取属性异常
*/
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeanException {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(clazz);
} catch (IntrospectionException e) {
throw new BeanException(e);
}
return ArrayUtil.filter(beanInfo.getPropertyDescriptors(), t -> {
// 过滤掉getClass方法
return false == "class".equals(t.getName());
});
}
/**
* 获得字段名和字段描述Map,获得的结果会缓存在 {@link BeanInfoCache}中
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws BeanException 获取属性异常
*/
public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> clazz, boolean ignoreCase) throws BeanException {
return BeanInfoCache.INSTANCE.getPropertyDescriptorMap(clazz, ignoreCase, () -> internalGetPropertyDescriptorMap(clazz, ignoreCase));
}
/**
* 获得字段名和字段描述Map。内部使用,直接获取Bean类的PropertyDescriptor
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws BeanException 获取属性异常
*/
private static Map<String, PropertyDescriptor> internalGetPropertyDescriptorMap(Class<?> clazz, boolean ignoreCase) throws BeanException {
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
final Map<String, PropertyDescriptor> map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1f)
: new HashMap<>(propertyDescriptors.length, 1);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
map.put(propertyDescriptor.getName(), propertyDescriptor);
}
return map;
}
/**
* 获得Bean类属性描述,大小写敏感
*
* @param clazz Bean类
* @param fieldName 字段名
* @return PropertyDescriptor
* @throws BeanException 获取属性异常
*/
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, final String fieldName) throws BeanException {
return getPropertyDescriptor(clazz, fieldName, false);
}
/**
* 获得Bean类属性描述
*
* @param clazz Bean类
* @param fieldName 字段名
* @param ignoreCase 是否忽略大小写
* @return PropertyDescriptor
* @throws BeanException 获取属性异常
*/
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, final String fieldName, boolean ignoreCase) throws BeanException {
final Map<String, PropertyDescriptor> map = getPropertyDescriptorMap(clazz, ignoreCase);
return (null == map) ? null : map.get(fieldName);
}
/**
* 获得字段值,通过反射直接获得字段值,并不调用getXXX方法<br>
* 对象同样支持Map类型,fieldNameOrIndex即为key
*
* <ul>
* <li>Map: fieldNameOrIndex需为key,获取对应value</li>
* <li>Collection: fieldNameOrIndex当为数字,返回index对应值,非数字遍历集合返回子bean对应name值</li>
* <li>Array: fieldNameOrIndex当为数字,返回index对应值,非数字遍历数组返回子bean对应name值</li>
* </ul>
*
* @param bean Bean对象
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @return 字段值
*/
public static Object getFieldValue(Object bean, String fieldNameOrIndex) {
if (null == bean || null == fieldNameOrIndex) {
return null;
}
if (bean instanceof Map) {
return ((Map<?, ?>) bean).get(fieldNameOrIndex);
} else if (bean instanceof Collection) {
try {
return CollUtil.get((Collection<?>) bean, Integer.parseInt(fieldNameOrIndex));
} catch (NumberFormatException e) {
// 非数字,see pr#254@Gitee
return CollUtil.map((Collection<?>) bean, (beanEle) -> getFieldValue(beanEle, fieldNameOrIndex), false);
}
} else if (ArrayUtil.isArray(bean)) {
try {
return ArrayUtil.get(bean, Integer.parseInt(fieldNameOrIndex));
} catch (NumberFormatException e) {
// 非数字,see pr#254@Gitee
return ArrayUtil.map(bean, Object.class, (beanEle) -> getFieldValue(beanEle, fieldNameOrIndex));
}
} else {// 普通Bean对象
return ReflectUtil.getFieldValue(bean, fieldNameOrIndex);
}
}
/**
* 设置字段值,通过反射设置字段值,并不调用setXXX方法<br>
* 对象同样支持Map类型,fieldNameOrIndex即为key,支持:
* <ul>
* <li>Map</li>
* <li>List</li>
* <li>Bean</li>
* </ul>
*
* @param bean Bean
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @param value 值
* @return bean,当为数组时,返回一个新的数组
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static Object setFieldValue(Object bean, String fieldNameOrIndex, Object value) {
if (bean instanceof Map) {
((Map) bean).put(fieldNameOrIndex, value);
} else if (bean instanceof List) {
ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value);
} else if (ArrayUtil.isArray(bean)) {
// issue#3008,追加产生新数组,此处返回新数组
return ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
} else {
// 普通Bean对象
ReflectUtil.setFieldValue(bean, fieldNameOrIndex, value);
}
return bean;
}
/**
* 解析Bean中的属性值
*
* @param <T> 属性值类型
* @param bean Bean对象,支持Map、List、Collection、Array
* @param expression 表达式,例如:person.friend[5].name
* @return Bean属性值,bean为{@code null}或者express为空,返回{@code null}
* @see BeanPath#get(Object)
* @since 3.0.7
*/
@SuppressWarnings("unchecked")
public static <T> T getProperty(Object bean, String expression) {
if (null == bean || StrUtil.isBlank(expression)) {
return null;
}
return (T) BeanPath.create(expression).get(bean);
}
/**
* 解析Bean中的属性值
*
* @param bean Bean对象,支持Map、List、Collection、Array
* @param expression 表达式,例如:person.friend[5].name
* @param value 属性值
* @see BeanPath#get(Object)
* @since 4.0.6
*/
public static void setProperty(Object bean, String expression, Object value) {
BeanPath.create(expression).set(bean, value);
}
// --------------------------------------------------------------------------------------------- mapToBean
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
* @deprecated 请使用 {@link #toBean(Object, Class)} 或 {@link #toBeanIgnoreError(Object, Class)}
*/
@Deprecated
public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, boolean isIgnoreError) {
return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
}
/**
* Map转换为Bean对象<br>
* 忽略大小写
*
* @param <T> Bean类型
* @param map Map
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
* @deprecated 请使用 {@link #toBeanIgnoreCase(Object, Class, boolean)}
*/
@Deprecated
public static <T> T mapToBeanIgnoreCase(Map<?, ?> map, Class<T> beanClass, boolean isIgnoreError) {
return fillBeanWithMapIgnoreCase(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
}
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param copyOptions 转Bean选项
* @return Bean
* @deprecated 请使用 {@link #toBean(Object, Class, CopyOptions)}
*/
@Deprecated
public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, CopyOptions copyOptions) {
return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), copyOptions);
}
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
* @param copyOptions 转Bean选项
* @return Bean
*/
public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, boolean isToCamelCase, CopyOptions copyOptions) {
return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isToCamelCase, copyOptions);
}
// --------------------------------------------------------------------------------------------- fillBeanWithMap
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static <T> T fillBeanWithMap(Map<?, ?> map, T bean, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, false, isIgnoreError);
}
/**
* 使用Map填充Bean对象,可配置将下划线转换为驼峰
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将下划线模式转换为驼峰模式
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static <T> T fillBeanWithMap(Map<?, ?> map, T bean, boolean isToCamelCase, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.create().setIgnoreError(isIgnoreError));
}
/**
* 使用Map填充Bean对象,忽略大小写
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static <T> T fillBeanWithMapIgnoreCase(Map<?, ?> map, T bean, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, CopyOptions.create().setIgnoreCase(true).setIgnoreError(isIgnoreError));
}
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
*/
public static <T> T fillBeanWithMap(Map<?, ?> map, T bean, CopyOptions copyOptions) {
return fillBeanWithMap(map, bean, false, copyOptions);
}
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
* @since 3.3.1
*/
public static <T> T fillBeanWithMap(Map<?, ?> map, T bean, boolean isToCamelCase, CopyOptions copyOptions) {
if (MapUtil.isEmpty(map)) {
return bean;
}
if (isToCamelCase) {
map = MapUtil.toCamelCaseMap(map);
}
copyProperties(map, bean, copyOptions);
return bean;
}
// --------------------------------------------------------------------------------------------- fillBean
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @return Bean对象
* @since 4.1.20
*/
public static <T> T toBean(Object source, Class<T> clazz) {
return toBean(source, clazz, null);
}
/**
* 对象或Map转Bean,忽略字段转换时发生的异常
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @return Bean对象
* @since 5.4.0
*/
public static <T> T toBeanIgnoreError(Object source, Class<T> clazz) {
return toBean(source, clazz, CopyOptions.create().setIgnoreError(true));
}
/**
* 对象或Map转Bean,忽略字段转换时发生的异常
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @param ignoreError 是否忽略注入错误
* @return Bean对象
* @since 5.4.0
*/
public static <T> T toBeanIgnoreCase(Object source, Class<T> clazz, boolean ignoreError) {
return toBean(source, clazz,
CopyOptions.create()
.setIgnoreCase(true)
.setIgnoreError(ignoreError));
}
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @param options 属性拷贝选项
* @return Bean对象
* @since 5.2.4
*/
public static <T> T toBean(Object source, Class<T> clazz, CopyOptions options) {
return toBean(source, () -> ReflectUtil.newInstanceIfPossible(clazz), options);
}
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param targetSupplier 目标的Bean创建器
* @param options 属性拷贝选项
* @return Bean对象
* @since 5.8.0
*/
public static <T> T toBean(Object source, Supplier<T> targetSupplier, CopyOptions options) {
if (null == source || null == targetSupplier) {
return null;
}
final T target = targetSupplier.get();
copyProperties(source, target, options);
return target;
}
/**
* ServletRequest 参数转Bean
*
* @param <T> Bean类型
* @param beanClass Bean Class
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public static <T> T toBean(Class<T> beanClass, ValueProvider<String> valueProvider, CopyOptions copyOptions) {
if (null == beanClass || null == valueProvider) {
return null;
}
return fillBean(ReflectUtil.newInstanceIfPossible(beanClass), valueProvider, copyOptions);
}
/**
* 填充Bean的核心方法
*
* @param <T> Bean类型
* @param bean Bean
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public static <T> T fillBean(T bean, ValueProvider<String> valueProvider, CopyOptions copyOptions) {
if (null == valueProvider) {
return bean;
}
return BeanCopier.create(valueProvider, bean, copyOptions).copy();
}
// --------------------------------------------------------------------------------------------- beanToMap
/**
* 将bean的部分属性转换成map<br>
* 可选拷贝哪些属性值,默认是不忽略值为{@code null}的值的。
*
* @param bean bean
* @param properties 需要拷贝的属性值,{@code null}或空表示拷贝所有值
* @return Map
* @since 5.8.0
*/
public static Map<String, Object> beanToMap(Object bean, String... properties) {
int mapSize = 16;
Editor<String> keyEditor = null;
if (ArrayUtil.isNotEmpty(properties)) {
mapSize = properties.length;
final Set<String> propertiesSet = CollUtil.set(false, properties);
keyEditor = property -> propertiesSet.contains(property) ? property : null;
}
// 指明了要复制的属性 所以不忽略null值
return beanToMap(bean, new LinkedHashMap<>(mapSize, 1), false, keyEditor);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
*/
public static Map<String, Object> beanToMap(Object bean, boolean isToUnderlineCase, boolean ignoreNullValue) {
if (null == bean) {
return null;
}
return beanToMap(bean, new LinkedHashMap<>(), isToUnderlineCase, ignoreNullValue);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
* @since 3.2.3
*/
public static Map<String, Object> beanToMap(Object bean, Map<String, Object> targetMap, final boolean isToUnderlineCase, boolean ignoreNullValue) {
if (null == bean) {
return null;
}
return beanToMap(bean, targetMap, ignoreNullValue, key -> isToUnderlineCase ? StrUtil.toUnderlineCase(key) : key);
}
/**
* 对象转Map<br>
* 通过实现{@link Editor} 可以自定义字段值,如果这个Editor返回null则忽略这个字段,以便实现:
*
* <pre>
* 1. 字段筛选,可以去除不需要的字段
* 2. 字段变换,例如实现驼峰转下划线
* 3. 自定义字段前缀或后缀等等
* </pre>
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param ignoreNullValue 是否忽略值为空的字段
* @param keyEditor 属性字段(Map的key)编辑器,用于筛选、编辑key,如果这个Editor返回null则忽略这个字段
* @return Map
* @since 4.0.5
*/
public static Map<String, Object> beanToMap(Object bean, Map<String, Object> targetMap, boolean ignoreNullValue, Editor<String> keyEditor) {
if (null == bean) {
return null;
}
return BeanCopier.create(bean, targetMap,
CopyOptions.create()
.setIgnoreNullValue(ignoreNullValue)
.setFieldNameEditor(keyEditor)
).copy();
}
/**
* 对象转Map<br>
* 通过自定义{@link CopyOptions} 完成抓换选项,以便实现:
*
* <pre>
* 1. 字段筛选,可以去除不需要的字段
* 2. 字段变换,例如实现驼峰转下划线
* 3. 自定义字段前缀或后缀等等
* 4. 字段值处理
* ...
* </pre>
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param copyOptions 拷贝选项
* @return Map
* @since 5.7.15
*/
public static Map<String, Object> beanToMap(Object bean, Map<String, Object> targetMap, CopyOptions copyOptions) {
if (null == bean) {
return null;
}
return BeanCopier.create(bean, targetMap, copyOptions).copy();
}
// --------------------------------------------------------------------------------------------- copyProperties
/**
* 按照Bean对象属性创建对应的Class对象,并忽略某些属性
*
* @param <T> 对象类型
* @param source 源Bean对象
* @param tClass 目标Class
* @param ignoreProperties 不拷贝的的属性列表
* @return 目标对象
*/
public static <T> T copyProperties(Object source, Class<T> tClass, String... ignoreProperties) {
if (null == source) {
return null;
}
T target = ReflectUtil.newInstanceIfPossible(tClass);
copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties));
return target;
}
/**
* 复制Bean对象属性<br>
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreProperties 不拷贝的的属性列表
*/
public static void copyProperties(Object source, Object target, String... ignoreProperties) {
copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties));
}
/**
* 复制Bean对象属性<br>
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreCase 是否忽略大小写
*/
public static void copyProperties(Object source, Object target, boolean ignoreCase) {
BeanCopier.create(source, target, CopyOptions.create().setIgnoreCase(ignoreCase)).copy();
}
/**
* 复制Bean对象属性<br>
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
*/
public static void copyProperties(Object source, Object target, CopyOptions copyOptions) {
if (null == source) {
return;
}
BeanCopier.create(source, target, ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create)).copy();
}
/**
* 复制集合中的Bean属性<br>
* 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。
*
* @param collection 原Bean集合
* @param targetType 目标Bean类型
* @param copyOptions 拷贝选项
* @param <T> Bean类型
* @return 复制后的List
* @since 5.6.4
*/
public static <T> List<T> copyToList(Collection<?> collection, Class<T> targetType, CopyOptions copyOptions) {
if (null == collection) {
return null;
}
if (collection.isEmpty()) {
return new ArrayList<>(0);
}
// issue#3091
if(ClassUtil.isBasicType(targetType) || String.class == targetType){
return Convert.toList(targetType, collection);
}
return collection.stream().map((source) -> {
final T target = ReflectUtil.newInstanceIfPossible(targetType);
copyProperties(source, target, copyOptions);
return target;
}).collect(Collectors.toList());
}
/**
* 复制集合中的Bean属性<br>
* 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。
*
* @param collection 原Bean集合
* @param targetType 目标Bean类型
* @param <T> Bean类型
* @return 复制后的List
* @since 5.6.6
*/
public static <T> List<T> copyToList(Collection<?> collection, Class<T> targetType) {
return copyToList(collection, targetType, CopyOptions.create());
}
/**
* 给定的Bean的类名是否匹配指定类名字符串<br>
* 如果isSimple为{@code true},则只匹配类名而忽略包名,例如:cn.hutool.TestEntity只匹配TestEntity<br>
* 如果isSimple为{@code false},则匹配包括包名的全类名,例如:cn.hutool.TestEntity匹配cn.hutool.TestEntity
*
* @param bean Bean
* @param beanClassName Bean的类名
* @param isSimple 是否只匹配类名而忽略包名,true表示忽略包名
* @return 是否匹配
* @since 4.0.6
*/
public static boolean isMatchName(Object bean, String beanClassName, boolean isSimple) {
if (null == bean || StrUtil.isBlank(beanClassName)) {
return false;
}
return ClassUtil.getClassName(bean, isSimple).equals(isSimple ? StrUtil.upperFirst(beanClassName) : beanClassName);
}
/**
* 编辑Bean的字段,static字段不会处理<br>
* 例如需要对指定的字段做判空操作、null转""操作等等。
*
* @param bean bean
* @param editor 编辑器函数
* @param <T> 被编辑的Bean类型
* @return bean
* @since 5.6.4
*/
public static <T> T edit(T bean, Editor<Field> editor) {
if (bean == null) {
return null;
}
final Field[] fields = ReflectUtil.getFields(bean.getClass());
for (Field field : fields) {
if (ModifierUtil.isStatic(field)) {
continue;
}
editor.edit(field);
}
return bean;
}
/**
* 把Bean里面的String属性做trim操作。此方法直接对传入的Bean做修改。
* <p>
* 通常bean直接用来绑定页面的input,用户的输入可能首尾存在空格,通常保存数据库前需要把首尾空格去掉
*
* @param <T> Bean类型
* @param bean Bean对象
* @param ignoreFields 不需要trim的Field名称列表(不区分大小写)
* @return 处理后的Bean对象
*/
public static <T> T trimStrFields(T bean, String... ignoreFields) {
return edit(bean, (field) -> {
if (ignoreFields != null && ArrayUtil.containsIgnoreCase(ignoreFields, field.getName())) {
// 不处理忽略的Fields
return field;
}
if (String.class.equals(field.getType())) {
// 只有String的Field才处理
final String val = (String) ReflectUtil.getFieldValue(bean, field);
if (null != val) {
final String trimVal = StrUtil.trim(val);
if (false == val.equals(trimVal)) {
// Field Value不为null,且首尾有空格才处理
ReflectUtil.setFieldValue(bean, field, trimVal);
}
}
}
return field;
});
}
/**
* 判断Bean是否为非空对象,非空对象表示本身不为{@code null}或者含有非{@code null}属性的对象
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否为非空,{@code true} - 非空 / {@code false} - 空
* @since 5.0.7
*/
public static boolean isNotEmpty(Object bean, String... ignoreFieldNames) {
return false == isEmpty(bean, ignoreFieldNames);
}
/**
* 判断Bean是否为空对象,空对象表示本身为{@code null}或者所有属性都为{@code null}<br>
* 此方法不判断static属性
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否为空,{@code true} - 空 / {@code false} - 非空
* @since 4.1.10
*/
public static boolean isEmpty(Object bean, String... ignoreFieldNames) {
if (null != bean) {
for (Field field : ReflectUtil.getFields(bean.getClass())) {
if (ModifierUtil.isStatic(field)) {
continue;
}
if ((false == ArrayUtil.contains(ignoreFieldNames, field.getName()))
&& null != ReflectUtil.getFieldValue(bean, field)) {
return false;
}
}
}
return true;
}
/**
* 判断Bean是否包含值为{@code null}的属性<br>
* 对象本身为{@code null}也返回true
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否包含值为<code>null</code>的属性,{@code true} - 包含 / {@code false} - 不包含
* @since 4.1.10
*/
public static boolean hasNullField(Object bean, String... ignoreFieldNames) {
if (null == bean) {
return true;
}
for (Field field : ReflectUtil.getFields(bean.getClass())) {
if (ModifierUtil.isStatic(field)) {
continue;
}
if ((false == ArrayUtil.contains(ignoreFieldNames, field.getName()))
&& null == ReflectUtil.getFieldValue(bean, field)) {
return true;
}
}
return false;
}
/**
* 获取Getter或Setter方法名对应的字段名称,规则如下:
* <ul>
* <li>getXxxx获取为xxxx,如getName得到name。</li>
* <li>setXxxx获取为xxxx,如setName得到name。</li>
* <li>isXxxx获取为xxxx,如isName得到name。</li>
* <li>其它不满足规则的方法名抛出{@link IllegalArgumentException}</li>
* </ul>
*
* @param getterOrSetterName Getter或Setter方法名
* @return 字段名称
* @throws IllegalArgumentException 非Getter或Setter方法
* @since 5.7.23
*/
public static String getFieldName(String getterOrSetterName) {
if (getterOrSetterName.startsWith("get") || getterOrSetterName.startsWith("set")) {
return StrUtil.removePreAndLowerFirst(getterOrSetterName, 3);
} else if (getterOrSetterName.startsWith("is")) {
return StrUtil.removePreAndLowerFirst(getterOrSetterName, 2);
} else {
throw new IllegalArgumentException("Invalid Getter or Setter name: " + getterOrSetterName);
}
}
/**
* 判断source与target的所有公共字段的值是否相同
*
* @param source 待检测对象1
* @param target 待检测对象2
* @param ignoreProperties 不需要检测的字段
* @return 判断结果,如果为true则证明所有字段的值都相同
* @author Takak11
* @since 5.8.4
*/
public static boolean isCommonFieldsEqual(Object source, Object target, String... ignoreProperties) {
if (null == source && null == target) {
return true;
}
if (null == source || null == target) {
return false;
}
Map<String, Object> sourceFieldsMap = BeanUtil.beanToMap(source);
Map<String, Object> targetFieldsMap = BeanUtil.beanToMap(target);
Set<String> sourceFields = sourceFieldsMap.keySet();
sourceFields.removeAll(Arrays.asList(ignoreProperties));
for (String field : sourceFields) {
if (ObjectUtil.notEqual(sourceFieldsMap.get(field), targetFieldsMap.get(field))) {
return false;
}
}
return true;
}
}
package cn.hutool.core.bean;
import cn.hutool.core.clone.CloneSupport;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import java.io.Serializable;
import java.util.Map;
/**
* 动态Bean,通过反射对Bean的相关方法做操作<br>
* 支持Map和普通Bean
*
* @author Looly
* @since 3.0.7
*/
public class DynaBean extends CloneSupport<DynaBean> implements Serializable {
private static final long serialVersionUID = 1L;
private final Class<?> beanClass;
private final Object bean;
/**
* 创建一个DynaBean
*
* @param bean 普通Bean
* @return DynaBean
*/
public static DynaBean create(Object bean) {
return new DynaBean(bean);
}
/**
* 创建一个DynaBean
*
* @param beanClass Bean类
* @return DynaBean
*/
public static DynaBean create(Class<?> beanClass) {
return new DynaBean(beanClass);
}
/**
* 创建一个DynaBean
*
* @param beanClass Bean类
* @param params 构造Bean所需要的参数
* @return DynaBean
*/
public static DynaBean create(Class<?> beanClass, Object... params) {
return new DynaBean(beanClass, params);
}
//------------------------------------------------------------------------ Constructor start
/**
* 构造
*
* @param beanClass Bean类
* @param params 构造Bean所需要的参数
*/
public DynaBean(Class<?> beanClass, Object... params) {
this(ReflectUtil.newInstance(beanClass, params));
}
/**
* 构造
*
* @param beanClass Bean类
*/
public DynaBean(Class<?> beanClass) {
this(ReflectUtil.newInstance(beanClass));
}
/**
* 构造
*
* @param bean 原始Bean
*/
public DynaBean(Object bean) {
Assert.notNull(bean);
if (bean instanceof DynaBean) {
bean = ((DynaBean) bean).getBean();
}
this.bean = bean;
this.beanClass = ClassUtil.getClass(bean);
}
//------------------------------------------------------------------------ Constructor end
/**
* 获得字段对应值
*
* @param <T> 属性值类型
* @param fieldName 字段名
* @return 字段值
* @throws BeanException 反射获取属性值或字段值导致的异常
*/
@SuppressWarnings("unchecked")
public <T> T get(String fieldName) throws BeanException {
if (Map.class.isAssignableFrom(beanClass)) {
return (T) ((Map<?, ?>) bean).get(fieldName);
} else {
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
if (null == prop) {
throw new BeanException("No public field or get method for {}", fieldName);
}
return (T) prop.getValue(bean);
}
}
/**
* 检查是否有指定名称的bean属性
*
* @param fieldName 字段名
* @return 是否有bean属性
* @since 5.4.2
*/
public boolean containsProp(String fieldName) {
if (Map.class.isAssignableFrom(beanClass)) {
return ((Map<?, ?>) bean).containsKey(fieldName);
} else{
return null != BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
}
}
/**
* 获得字段对应值,获取异常返回{@code null}
*
* @param <T> 属性值类型
* @param fieldName 字段名
* @return 字段值
* @since 3.1.1
*/
public <T> T safeGet(String fieldName) {
try {
return get(fieldName);
} catch (Exception e) {
return null;
}
}
/**
* 设置字段值
*
* @param fieldName 字段名
* @param value 字段值
* @throws BeanException 反射获取属性值或字段值导致的异常
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void set(String fieldName, Object value) throws BeanException {
if (Map.class.isAssignableFrom(beanClass)) {
((Map) bean).put(fieldName, value);
} else {
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
if (null == prop) {
throw new BeanException("No public field or set method for {}", fieldName);
}
prop.setValue(bean, value);
}
}
/**
* 执行原始Bean中的方法
*
* @param methodName 方法名
* @param params 参数
* @return 执行结果,可能为null
*/
public Object invoke(String methodName, Object... params) {
return ReflectUtil.invoke(this.bean, methodName, params);
}
/**
* 获得原始Bean
*
* @param <T> Bean类型
* @return bean
*/
@SuppressWarnings("unchecked")
public <T> T getBean() {
return (T) this.bean;
}
/**
* 获得Bean的类型
*
* @param <T> Bean类型
* @return Bean类型
*/
@SuppressWarnings("unchecked")
public <T> Class<T> getBeanClass() {
return (Class<T>) this.beanClass;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bean == null) ? 0 : bean.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DynaBean other = (DynaBean) obj;
if (bean == null) {
return other.bean == null;
} else return bean.equals(other.bean);
}
@Override
public String toString() {
return this.bean.toString();
}
}
package cn.hutool.core.bean;
/**
* 为了解决反射过程中,需要传递null参数,但是会丢失参数类型而设立的包装类
*
* @param <T> Null值对应的类型
* @author Lillls
* @since 5.5.0
*/
public class NullWrapperBean<T> {
private final Class<T> clazz;
/**
* @param clazz null的类型
*/
public NullWrapperBean(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 获取null值对应的类型
*
* @return 类型
*/
public Class<T> getWrappedClass() {
return clazz;
}
}
package cn.hutool.core.bean;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.annotation.PropIgnore;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import java.beans.Transient;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* 属性描述,包括了字段、getter、setter和相应的方法执行
*
* @author looly
*/
public class PropDesc {
/**
* 字段
*/
final Field field;
/**
* Getter方法
*/
protected Method getter;
/**
* Setter方法
*/
protected Method setter;
/**
* 构造<br>
* Getter和Setter方法设置为默认可访问
*
* @param field 字段
* @param getter get方法
* @param setter set方法
*/
public PropDesc(Field field, Method getter, Method setter) {
this.field = field;
this.getter = ClassUtil.setAccessible(getter);
this.setter = ClassUtil.setAccessible(setter);
}
/**
* 获取字段名,如果存在Alias注解,读取注解的值作为名称
*
* @return 字段名
*/
public String getFieldName() {
return ReflectUtil.getFieldName(this.field);
}
/**
* 获取字段名称
*
* @return 字段名
* @since 5.1.6
*/
public String getRawFieldName() {
return null == this.field ? null : this.field.getName();
}
/**
* 获取字段
*
* @return 字段
*/
public Field getField() {
return this.field;
}
/**
* 获得字段类型<br>
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public Type getFieldType() {
if (null != this.field) {
return TypeUtil.getType(this.field);
}
return findPropType(getter, setter);
}
/**
* 获得字段类型<br>
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public Class<?> getFieldClass() {
if (null != this.field) {
return TypeUtil.getClass(this.field);
}
return findPropClass(getter, setter);
}
/**
* 获取Getter方法,可能为{@code null}
*
* @return Getter方法
*/
public Method getGetter() {
return this.getter;
}
/**
* 获取Setter方法,可能为{@code null}
*
* @return {@link Method}Setter 方法对象
*/
public Method getSetter() {
return this.setter;
}
/**
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
*
* @param checkTransient 是否检查Transient关键字或注解
* @return 是否可读
* @since 5.4.2
*/
public boolean isReadable(boolean checkTransient) {
// 检查是否有getter方法或是否为public修饰
if (null == this.getter && false == ModifierUtil.isPublic(this.field)) {
return false;
}
// 检查transient关键字和@Transient注解
if (checkTransient && isTransientForGet()) {
return false;
}
// 检查@PropIgnore注解
return false == isIgnoreGet();
}
/**
* 获取属性值<br>
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值<br>
* 此方法不检查任何注解,使用前需调用 {@link #isReadable(boolean)} 检查是否可读
*
* @param bean Bean对象
* @return 字段值
* @since 4.0.5
*/
public Object getValue(Object bean) {
if (null != this.getter) {
return ReflectUtil.invoke(bean, this.getter);
} else if (ModifierUtil.isPublic(this.field)) {
return ReflectUtil.getFieldValue(bean, this.field);
}
return null;
}
/**
* 获取属性值,自动转换属性值类型<br>
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
*
* @param bean Bean对象
* @param targetType 返回属性值需要转换的类型,null表示不转换
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @return this
* @since 5.4.2
*/
public Object getValue(Object bean, Type targetType, boolean ignoreError) {
Object result = null;
try {
result = getValue(bean);
} catch (Exception e) {
if (false == ignoreError) {
throw new BeanException(e, "Get value of [{}] error!", getFieldName());
}
}
if (null != result && null != targetType) {
// 尝试将结果转换为目标类型,如果转换失败,返回null,即跳过此属性值。
// 来自:issues#I41WKP@Gitee,当忽略错误情况下,目标类型转换失败应返回null
// 如果返回原值,在集合注入时会成功,但是集合取值时会报类型转换错误
return Convert.convertWithCheck(targetType, result, null, ignoreError);
}
return result;
}
/**
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
*
* @param checkTransient 是否检查Transient关键字或注解
* @return 是否可读
* @since 5.4.2
*/
public boolean isWritable(boolean checkTransient) {
// 检查是否有getter方法或是否为public修饰
if (null == this.setter && false == ModifierUtil.isPublic(this.field)) {
return false;
}
// 检查transient关键字和@Transient注解
if (checkTransient && isTransientForSet()) {
return false;
}
// 检查@PropIgnore注解
return false == isIgnoreSet();
}
/**
* 设置Bean的字段值<br>
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值<br>
* 此方法不检查任何注解,使用前需调用 {@link #isWritable(boolean)} 检查是否可写
*
* @param bean Bean对象
* @param value 值,必须与字段值类型匹配
* @return this
* @since 4.0.5
*/
public PropDesc setValue(Object bean, Object value) {
if (null != this.setter) {
ReflectUtil.invoke(bean, this.setter, value);
} else if (ModifierUtil.isPublic(this.field)) {
ReflectUtil.setFieldValue(bean, this.field, value);
}
return this;
}
/**
* 设置属性值,可以自动转换字段类型为目标类型
*
* @param bean Bean对象
* @param value 属性值,可以为任意类型
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @return this
* @since 5.4.2
*/
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError) {
return setValue(bean, value, ignoreNull, ignoreError, true);
}
/**
* 设置属性值,可以自动转换字段类型为目标类型
*
* @param bean Bean对象
* @param value 属性值,可以为任意类型
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,{@code null}则写,否则忽略。如果覆盖,则不判断直接写
* @return this
* @since 5.7.17
*/
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError, boolean override) {
if (null == value && ignoreNull) {
return this;
}
// issue#I4JQ1N@Gitee
// 非覆盖模式下,如果目标值存在,则跳过
if (false == override && null != getValue(bean)) {
return this;
}
// 当类型不匹配的时候,执行默认转换
if (null != value) {
final Class<?> propClass = getFieldClass();
if (false == propClass.isInstance(value)) {
value = Convert.convertWithCheck(propClass, value, null, ignoreError);
}
}
// 属性赋值
if (null != value || false == ignoreNull) {
try {
this.setValue(bean, value);
} catch (Exception e) {
if (false == ignoreError) {
throw new BeanException(e, "Set value of [{}] error!", getFieldName());
}
// 忽略注入失败
}
}
return this;
}
//------------------------------------------------------------------------------------ Private method start
/**
* 通过Getter和Setter方法中找到属性类型
*
* @param getter Getter方法
* @param setter Setter方法
* @return {@link Type}
*/
private Type findPropType(Method getter, Method setter) {
Type type = null;
if (null != getter) {
type = TypeUtil.getReturnType(getter);
}
if (null == type && null != setter) {
type = TypeUtil.getParamType(setter, 0);
}
return type;
}
/**
* 通过Getter和Setter方法中找到属性类型
*
* @param getter Getter方法
* @param setter Setter方法
* @return {@link Type}
*/
private Class<?> findPropClass(Method getter, Method setter) {
Class<?> type = null;
if (null != getter) {
type = TypeUtil.getReturnClass(getter);
}
if (null == type && null != setter) {
type = TypeUtil.getFirstParamClass(setter);
}
return type;
}
/**
* 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在setXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略写
* @since 5.4.2
*/
private boolean isIgnoreSet() {
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|| AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
}
/**
* 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在getXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略读
* @since 5.4.2
*/
private boolean isIgnoreGet() {
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|| AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
}
/**
* 字段和Getter方法是否为Transient关键字修饰的
*
* @return 是否为Transient关键字修饰的
* @since 5.3.11
*/
private boolean isTransientForGet() {
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
// 检查Getter方法
if (false == isTransient && null != this.getter) {
isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT);
// 检查注解
if (false == isTransient) {
isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
}
}
return isTransient;
}
/**
* 字段和Getter方法是否为Transient关键字修饰的
*
* @return 是否为Transient关键字修饰的
* @since 5.3.11
*/
private boolean isTransientForSet() {
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
// 检查Getter方法
if (false == isTransient && null != this.setter) {
isTransient = ModifierUtil.hasModifier(this.setter, ModifierUtil.ModifierType.TRANSIENT);
// 检查注解
if (false == isTransient) {
isTransient = AnnotationUtil.hasAnnotation(this.setter, Transient.class);
}
}
return isTransient;
}
//------------------------------------------------------------------------------------ Private method end
}
package cn.hutool.core.bean.copier;
import cn.hutool.core.lang.copier.Copier;
import cn.hutool.core.util.ObjectUtil;
/**
* 抽象的对象拷贝封装,提供来源对象、目标对象持有
*
* @param <S> 来源对象类型
* @param <T> 目标对象类型
* @author looly
* @since 5.8.0
*/
public abstract class AbsCopier<S, T> implements Copier<T> {
protected final S source;
protected final T target;
/**
* 拷贝选项
*/
protected final CopyOptions copyOptions;
public AbsCopier(S source, T target, CopyOptions copyOptions) {
this.source = source;
this.target = target;
this.copyOptions = ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create);
}
}
package cn.hutool.core.bean.copier;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.copier.Copier;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Bean拷贝,提供:
*
* <pre>
* 1. Bean 转 Bean
* 2. Bean 转 Map
* 3. Map 转 Bean
* 4. Map 转 Map
* </pre>
*
* @author looly
*
* @param <T> 目标对象类型
* @since 3.2.3
*/
public class BeanCopier<T> implements Copier<T>, Serializable {
private static final long serialVersionUID = 1L;
private final Copier<T> copier;
/**
* 创建BeanCopier
*
* @param <T> 目标Bean类型
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param copyOptions 拷贝属性选项
* @return BeanCopier
*/
public static <T> BeanCopier<T> create(Object source, T target, CopyOptions copyOptions) {
return create(source, target, target.getClass(), copyOptions);
}
/**
* 创建BeanCopier
*
* @param <T> 目标Bean类型
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param destType 目标的泛型类型,用于标注有泛型参数的Bean对象
* @param copyOptions 拷贝属性选项
* @return BeanCopier
*/
public static <T> BeanCopier<T> create(Object source, T target, Type destType, CopyOptions copyOptions) {
return new BeanCopier<>(source, target, destType, copyOptions);
}
/**
* 构造
*
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
* @param copyOptions 拷贝属性选项
*/
public BeanCopier(Object source, T target, Type targetType, CopyOptions copyOptions) {
Assert.notNull(source, "Source bean must be not null!");
Assert.notNull(target, "Target bean must be not null!");
Copier<T> copier;
if (source instanceof Map) {
if (target instanceof Map) {
//noinspection unchecked
copier = (Copier<T>) new MapToMapCopier((Map<?, ?>) source, (Map<?, ?>) target, targetType, copyOptions);
} else {
copier = new MapToBeanCopier<>((Map<?, ?>) source, target, targetType, copyOptions);
}
}else if(source instanceof ValueProvider){
//noinspection unchecked
copier = new ValueProviderToBeanCopier<>((ValueProvider<String>) source, target, targetType, copyOptions);
} else {
if (target instanceof Map) {
//noinspection unchecked
copier = (Copier<T>) new BeanToMapCopier(source, (Map<?, ?>) target, targetType, copyOptions);
} else {
copier = new BeanToBeanCopier<>(source, target, targetType, copyOptions);
}
}
this.copier = copier;
}
@Override
public T copy() {
return copier.copy();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment