annotationType) {
+ return AnnotationScanner.DIRECTLY
+ .getAnnotationsIfSupport(annotatedEle).stream()
+ .map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 对指定注解对象进行聚合
+ *
+ * @param annotations 注解对象
+ * @return 聚合注解
+ */
+ public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) {
+ return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.NOTHING);
+ }
+
+ /**
+ * 对指定注解对象及其元注解进行聚合
+ *
+ * @param annotations 注解对象
+ * @return 聚合注解
+ */
+ public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) {
+ return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.DIRECTLY_AND_META_ANNOTATION);
+ }
+
+ /**
+ * 方法是否为注解属性方法。
+ * 方法无参数,且有返回值的方法认为是注解属性的方法。
+ *
+ * @param method 方法
+ */
+ static boolean isAttributeMethod(Method method) {
+ return method.getParameterCount() == 0 && method.getReturnType() != void.class;
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java b/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java
new file mode 100644
index 0000000..e4c8bc9
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java
@@ -0,0 +1,63 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * {@link AnnotationAttribute}的基本实现
+ *
+ * @author huangchengxing
+ */
+public class CacheableAnnotationAttribute implements AnnotationAttribute {
+
+ private boolean valueInvoked;
+ private Object value;
+
+ private boolean defaultValueInvoked;
+ private Object defaultValue;
+
+ private final Annotation annotation;
+ private final Method attribute;
+
+ public CacheableAnnotationAttribute(Annotation annotation, Method attribute) {
+ Assert.notNull(annotation, "annotation must not null");
+ Assert.notNull(attribute, "attribute must not null");
+ this.annotation = annotation;
+ this.attribute = attribute;
+ this.valueInvoked = false;
+ this.defaultValueInvoked = false;
+ }
+
+ @Override
+ public Annotation getAnnotation() {
+ return this.annotation;
+ }
+
+ @Override
+ public Method getAttribute() {
+ return this.attribute;
+ }
+
+ @Override
+ public Object getValue() {
+ if (!valueInvoked) {
+ valueInvoked = true;
+ value = ReflectUtil.invoke(annotation, attribute);
+ }
+ return value;
+ }
+
+ @Override
+ public boolean isValueEquivalentToDefaultValue() {
+ if (!defaultValueInvoked) {
+ defaultValue = attribute.getDefaultValue();
+ defaultValueInvoked = true;
+ }
+ return ObjectUtil.equals(getValue(), defaultValue);
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java b/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java
new file mode 100644
index 0000000..ab7ae9a
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java
@@ -0,0 +1,62 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.multi.RowKeyTable;
+import cn.hutool.core.map.multi.Table;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+/**
+ * 带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现,
+ * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序,
+ * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值
+ *
+ *
通过该处理器获取合成注解属性值时会出现隐式别名,
+ * 即子注解和元注解中同时存在类型和名称皆相同的属性时,元注解中属性总是会被该属性覆盖,
+ * 并且该覆盖关系并不会通过{@link Alias}或{@link Link}被传递到关联的属性中。
+ *
+ * @author huangchengxing
+ */
+public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor {
+
+ private final Table, Object> valueCaches = new RowKeyTable<>();
+ private final Comparator annotationComparator;
+
+ /**
+ * 创建一个带缓存的注解值选择器
+ *
+ * @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值
+ */
+ public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) {
+ Assert.notNull(annotationComparator, "annotationComparator must not null");
+ this.annotationComparator = annotationComparator;
+ }
+
+ /**
+ * 创建一个带缓存的注解值选择器,
+ * 默认按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序,
+ * 越靠前的越优先被取值。
+ */
+ public CacheableSynthesizedAnnotationAttributeProcessor() {
+ this(Hierarchical.DEFAULT_HIERARCHICAL_COMPARATOR);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getAttributeValue(String attributeName, Class attributeType, Collection extends SynthesizedAnnotation> synthesizedAnnotations) {
+ Object value = valueCaches.get(attributeName, attributeType);
+ // 此处理论上不可能出现缓存值为nul的情况
+ if (ObjectUtil.isNotNull(value)) {
+ return (T)value;
+ }
+ value = synthesizedAnnotations.stream()
+ .filter(ma -> ma.hasAttribute(attributeName, attributeType))
+ .min(annotationComparator)
+ .map(ma -> ma.getAttributeValue(attributeName))
+ .orElse(null);
+ valueCaches.put(attributeName, attributeType, value);
+ return (T)value;
+ }
+}
diff --git a/src/main/java/cn/hutool/core/annotation/CombinationAnnotationElement.java b/src/main/java/cn/hutool/core/annotation/CombinationAnnotationElement.java
new file mode 100644
index 0000000..e5c1f47
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/CombinationAnnotationElement.java
@@ -0,0 +1,165 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.map.TableMap;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Predicate;
+
+/**
+ * 组合注解 对JDK的原生注解机制做一个增强,支持类似Spring的组合注解。
+ * 核心实现使用了递归获取指定元素上的注解以及注解的注解,以实现复合注解的获取。
+ *
+ * @author Succy, Looly
+ * @since 4.0.9
+ **/
+
+public class CombinationAnnotationElement implements AnnotatedElement, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建CombinationAnnotationElement
+ *
+ * @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
+ * @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
+ * @return CombinationAnnotationElement
+ * @since 5.8.0
+ */
+ public static CombinationAnnotationElement of(AnnotatedElement element, Predicate predicate) {
+ return new CombinationAnnotationElement(element, predicate);
+ }
+
+ /**
+ * 注解类型与注解对象对应表
+ */
+ private Map, Annotation> annotationMap;
+ /**
+ * 直接注解类型与注解对象对应表
+ */
+ private Map, Annotation> declaredAnnotationMap;
+ /**
+ * 过滤器
+ */
+ private final Predicate predicate;
+
+ /**
+ * 构造
+ *
+ * @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
+ */
+ public CombinationAnnotationElement(AnnotatedElement element) {
+ this(element, null);
+ }
+
+ /**
+ * 构造
+ *
+ * @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
+ * @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
+ * @since 5.8.0
+ */
+ public CombinationAnnotationElement(AnnotatedElement element, Predicate predicate) {
+ this.predicate = predicate;
+ init(element);
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
+ return annotationMap.containsKey(annotationClass);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getAnnotation(Class annotationClass) {
+ Annotation annotation = annotationMap.get(annotationClass);
+ return (annotation == null) ? null : (T) annotation;
+ }
+
+ @Override
+ public Annotation[] getAnnotations() {
+ final Collection annotations = this.annotationMap.values();
+ return annotations.toArray(new Annotation[0]);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ final Collection annotations = this.declaredAnnotationMap.values();
+ return annotations.toArray(new Annotation[0]);
+ }
+
+ /**
+ * 初始化
+ *
+ * @param element 元素
+ */
+ private void init(AnnotatedElement element) {
+ final Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
+ this.declaredAnnotationMap = new TableMap<>();
+ parseDeclared(declaredAnnotations);
+
+ final Annotation[] annotations = element.getAnnotations();
+ if (Arrays.equals(declaredAnnotations, annotations)) {
+ this.annotationMap = this.declaredAnnotationMap;
+ } else {
+ this.annotationMap = new TableMap<>();
+ parse(annotations);
+ }
+ }
+
+ /**
+ * 进行递归解析注解,直到全部都是元注解为止
+ *
+ * @param annotations Class, Method, Field等
+ */
+ private void parseDeclared(Annotation[] annotations) {
+ Class extends Annotation> annotationType;
+ // 直接注解
+ for (Annotation annotation : annotations) {
+ annotationType = annotation.annotationType();
+ // issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
+ if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
+ && !declaredAnnotationMap.containsKey(annotationType)) {
+ if(test(annotation)){
+ declaredAnnotationMap.put(annotationType, annotation);
+ }
+ // 测试不通过的注解,不影响继续递归
+ parseDeclared(annotationType.getDeclaredAnnotations());
+ }
+ }
+ }
+
+ /**
+ * 进行递归解析注解,直到全部都是元注解为止
+ *
+ * @param annotations Class, Method, Field等
+ */
+ private void parse(Annotation[] annotations) {
+ Class extends Annotation> annotationType;
+ for (Annotation annotation : annotations) {
+ annotationType = annotation.annotationType();
+ // issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
+ if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
+ && !declaredAnnotationMap.containsKey(annotationType)) {
+ if(test(annotation)){
+ annotationMap.put(annotationType, annotation);
+ }
+ // 测试不通过的注解,不影响继续递归
+ parse(annotationType.getAnnotations());
+ }
+ }
+ }
+
+ /**
+ * 检查给定的注解是否符合过滤条件
+ *
+ * @param annotation 注解对象
+ * @return 是否符合条件
+ */
+ private boolean test(Annotation annotation) {
+ return null == this.predicate || this.predicate.test(annotation);
+ }
+}
diff --git a/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java b/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java
new file mode 100644
index 0000000..7549a15
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java
@@ -0,0 +1,35 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link Link}的子注解。表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
+ * 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
+ * 注意,该注解与{@link Link}、{@link AliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效
+ *
+ * @author huangchengxing
+ * @see Link
+ * @see RelationType#FORCE_ALIAS_FOR
+ */
+@Link(type = RelationType.FORCE_ALIAS_FOR)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface ForceAliasFor {
+
+ /**
+ * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
+ *
+ * @return 关联注解类型
+ */
+ @Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
+ Class extends Annotation> annotation() default Annotation.class;
+
+ /**
+ * {@link #annotation()}指定注解中关联的属性
+ *
+ * @return 关联的属性
+ */
+ @Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
+ String attribute() default "";
+}
diff --git a/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java b/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java
new file mode 100644
index 0000000..312219c
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java
@@ -0,0 +1,49 @@
+package cn.hutool.core.annotation;
+
+/**
+ * 表示一个被指定了强制别名的注解属性。
+ * 当调用{@link #getValue()}时,总是返回{@link #linked}的值
+ *
+ * @author huangchengxing
+ * @see AliasAnnotationPostProcessor
+ * @see AliasLinkAnnotationPostProcessor
+ * @see RelationType#ALIAS_FOR
+ * @see RelationType#FORCE_ALIAS_FOR
+ */
+public class ForceAliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
+
+ protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
+ super(origin, linked);
+ }
+
+ /**
+ * 总是返回{@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
+ *
+ * @return {@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
+ */
+ @Override
+ public Object getValue() {
+ return linked.getValue();
+ }
+
+ /**
+ * 总是返回{@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
+ *
+ * @return {@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
+ */
+ @Override
+ public boolean isValueEquivalentToDefaultValue() {
+ return linked.isValueEquivalentToDefaultValue();
+ }
+
+ /**
+ * 总是返回{@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
+ *
+ * @return {@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
+ */
+ @Override
+ public Class> getAttributeType() {
+ return linked.getAttributeType();
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java b/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java
new file mode 100644
index 0000000..8a5f7b7
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java
@@ -0,0 +1,318 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.annotation.scanner.AnnotationScanner;
+import cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.Opt;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.*;
+
+/**
+ * {@link SynthesizedAggregateAnnotation}的基本实现,表示基于多个注解对象,
+ * 或多个根注解对象与他们的多层元注解对象的聚合得到的注解。
+ *
+ *
假设现有注解A,若指定的{@link #annotationScanner}支持扫描注解A的元注解,
+ * 且A上存在元注解B,B上存在元注解C,则对注解A进行解析,将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link GenericSynthesizedAggregateAnnotation}。
+ * 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素,
+ * 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。
+ *
+ *
在扫描指定根注解及其元注解时,若在不同的层级出现了类型相同的注解实例,
+ * 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解,
+ * 完成对根注解及其元注解的扫描后,合成注解中每种类型的注解对象都将有且仅有一个。
+ * 默认情况下,将使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器,
+ * 此时若出现扫描时得到了多个同类型的注解对象,有且仅有最接近根注解的注解对象会被作为有效注解。
+ *
+ *
当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后,
+ * 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAnnotationPostProcessor}
+ * 进行后置处理。
+ * 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持:
+ *
+ * - {@link AliasAnnotationPostProcessor};
+ * - {@link MirrorLinkAnnotationPostProcessor};
+ * - {@link AliasLinkAnnotationPostProcessor};
+ *
+ * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。
+ *
+ * {@link GenericSynthesizedAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)},
+ * 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值,
+ * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。
+ * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
+ * 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor},
+ * 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖,并且缓存最终获取到的属性值。
+ *
+ * @author huangchengxing
+ * @see AnnotationUtil
+ * @see SynthesizedAnnotationProxy
+ * @see SynthesizedAnnotationSelector
+ * @see SynthesizedAnnotationAttributeProcessor
+ * @see SynthesizedAnnotationPostProcessor
+ * @see AnnotationSynthesizer
+ * @see AnnotationScanner
+ */
+public class GenericSynthesizedAggregateAnnotation
+ extends AbstractAnnotationSynthesizer>
+ implements SynthesizedAggregateAnnotation {
+
+ /**
+ * 根对象
+ */
+ private final Object root;
+
+ /**
+ * 距离根对象的垂直距离
+ */
+ private final int verticalDistance;
+
+ /**
+ * 距离根对象的水平距离
+ */
+ private final int horizontalDistance;
+
+ /**
+ * 合成注解属性处理器
+ */
+ private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
+
+ /**
+ * 基于指定根注解,为其与其元注解的层级结构中的全部注解构造一个合成注解。
+ * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象,
+ * 当获取值时,同样遵循该规则。
+ *
+ * @param source 源注解
+ */
+ public GenericSynthesizedAggregateAnnotation(Annotation... source) {
+ this(Arrays.asList(source), new MetaAnnotationScanner());
+ }
+
+ /**
+ * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。
+ * 若扫描器支持对注解的层级结构进行扫描,则若层级结构中出现了相同的注解对象时,
+ * 将优先选择以距离根注解最近,且优先被扫描的注解对象,并且当获取注解属性值时同样遵循该规则。
+ *
+ * @param source 源注解
+ * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
+ */
+ public GenericSynthesizedAggregateAnnotation(List source, AnnotationScanner annotationScanner) {
+ this(
+ source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
+ new CacheableSynthesizedAnnotationAttributeProcessor(),
+ Arrays.asList(
+ SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR,
+ SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR,
+ SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR
+ ),
+ annotationScanner
+ );
+ }
+
+ /**
+ * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
+ *
+ * @param source 当前查找的注解对象
+ * @param annotationSelector 合成注解选择器
+ * @param attributeProcessor 注解属性处理器
+ * @param annotationPostProcessors 注解后置处理器
+ * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
+ */
+ public GenericSynthesizedAggregateAnnotation(
+ List source,
+ SynthesizedAnnotationSelector annotationSelector,
+ SynthesizedAnnotationAttributeProcessor attributeProcessor,
+ Collection annotationPostProcessors,
+ AnnotationScanner annotationScanner) {
+ this(
+ null, 0, 0,
+ source, annotationSelector, attributeProcessor, annotationPostProcessors, annotationScanner
+ );
+ }
+
+ /**
+ * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
+ *
+ * @param root 根对象
+ * @param verticalDistance 距离根对象的水平距离
+ * @param horizontalDistance 距离根对象的垂直距离
+ * @param source 当前查找的注解对象
+ * @param annotationSelector 合成注解选择器
+ * @param attributeProcessor 注解属性处理器
+ * @param annotationPostProcessors 注解后置处理器
+ * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
+ */
+ GenericSynthesizedAggregateAnnotation(
+ Object root, int verticalDistance, int horizontalDistance,
+ List source,
+ SynthesizedAnnotationSelector annotationSelector,
+ SynthesizedAnnotationAttributeProcessor attributeProcessor,
+ Collection annotationPostProcessors,
+ AnnotationScanner annotationScanner) {
+ super(source, annotationSelector, annotationPostProcessors, annotationScanner);
+ Assert.notNull(attributeProcessor, "attributeProcessor must not null");
+
+ this.root = ObjectUtil.defaultIfNull(root, this);
+ this.verticalDistance = verticalDistance;
+ this.horizontalDistance = horizontalDistance;
+ this.attributeProcessor = attributeProcessor;
+ }
+
+ /**
+ * 获取根对象
+ *
+ * @return 根对象
+ */
+ @Override
+ public Object getRoot() {
+ return root;
+ }
+
+ /**
+ * 获取与根对象的垂直距离
+ *
+ * @return 与根对象的垂直距离
+ */
+ @Override
+ public int getVerticalDistance() {
+ return verticalDistance;
+ }
+
+ /**
+ * 获取与根对象的水平距离
+ *
+ * @return 获取与根对象的水平距离
+ */
+ @Override
+ public int getHorizontalDistance() {
+ return horizontalDistance;
+ }
+
+ /**
+ * 按广度优先扫描{@link #source}上的元注解
+ */
+ @Override
+ protected Map, SynthesizedAnnotation> loadAnnotations() {
+ Map, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>();
+
+ // 根注解默认水平坐标为0,根注解的元注解坐标从1开始
+ for (int i = 0; i < source.size(); i++) {
+ final Annotation sourceAnnotation = source.get(i);
+ Assert.isFalse(AnnotationUtil.isSynthesizedAnnotation(sourceAnnotation), "source [{}] has been synthesized");
+ annotationMap.put(sourceAnnotation.annotationType(), new MetaAnnotation(sourceAnnotation, sourceAnnotation, 0, i));
+ Assert.isTrue(
+ annotationScanner.support(sourceAnnotation.annotationType()),
+ "annotation scanner [{}] cannot support scan [{}]",
+ annotationScanner, sourceAnnotation.annotationType()
+ );
+ annotationScanner.scan(
+ (index, annotation) -> {
+ SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType());
+ SynthesizedAnnotation newAnnotation = new MetaAnnotation(sourceAnnotation, annotation, index + 1, annotationMap.size());
+ if (ObjectUtil.isNull(oldAnnotation)) {
+ annotationMap.put(annotation.annotationType(), newAnnotation);
+ } else {
+ annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation));
+ }
+ },
+ sourceAnnotation.annotationType(), null
+ );
+ }
+ return annotationMap;
+ }
+
+ /**
+ * 获取合成注解属性处理器
+ *
+ * @return 合成注解属性处理器
+ */
+ @Override
+ public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
+ return this.attributeProcessor;
+ }
+
+ /**
+ * 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
+ * 当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
+ *
+ * @param attributeName 属性名
+ * @param attributeType 属性类型
+ * @return 属性
+ */
+ @Override
+ public Object getAttributeValue(String attributeName, Class> attributeType) {
+ return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values());
+ }
+
+ /**
+ * 获取合成注解中包含的指定注解
+ *
+ * @param annotationType 注解类型
+ * @param 注解类型
+ * @return 注解对象
+ */
+ @Override
+ public T getAnnotation(Class annotationType) {
+ return Opt.ofNullable(annotationType)
+ .map(synthesizedAnnotationMap::get)
+ .map(SynthesizedAnnotation::getAnnotation)
+ .map(annotationType::cast)
+ .orElse(null);
+ }
+
+ /**
+ * 当前合成注解中是否存在指定元注解
+ *
+ * @param annotationType 注解类型
+ * @return 是否
+ */
+ @Override
+ public boolean isAnnotationPresent(Class extends Annotation> annotationType) {
+ return synthesizedAnnotationMap.containsKey(annotationType);
+ }
+
+ /**
+ * 获取合成注解中包含的全部注解
+ *
+ * @return 注解对象
+ */
+ @Override
+ public Annotation[] getAnnotations() {
+ return synthesizedAnnotationMap.values().stream()
+ .map(SynthesizedAnnotation::getAnnotation)
+ .toArray(Annotation[]::new);
+ }
+
+ /**
+ * 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
+ *
+ * @param annotationType 注解类型
+ * @return 合成注解对象
+ * @see SynthesizedAnnotationProxy#create(Class, AnnotationAttributeValueProvider, SynthesizedAnnotation)
+ */
+ @Override
+ public T synthesize(Class annotationType, SynthesizedAnnotation annotation) {
+ return SynthesizedAnnotationProxy.create(annotationType, this, annotation);
+ }
+
+ /**
+ * 注解包装类,表示{@link #source}以及{@link #source}所属层级结构中的全部关联注解对象
+ *
+ * @author huangchengxing
+ */
+ public static class MetaAnnotation extends GenericSynthesizedAnnotation {
+
+ /**
+ * 创建一个合成注解
+ *
+ * @param root 根对象
+ * @param annotation 被合成的注解对象
+ * @param verticalDistance 距离根对象的水平距离
+ * @param horizontalDistance 距离根对象的垂直距离
+ */
+ protected MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
+ super(root, annotation, verticalDistance, horizontalDistance);
+ }
+
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java b/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java
new file mode 100644
index 0000000..3034249
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java
@@ -0,0 +1,197 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Opt;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * {@link SynthesizedAnnotation}的基本实现
+ *
+ * @param 根对象类型
+ * @param 注解类型
+ * @author huangchengxing
+ */
+public class GenericSynthesizedAnnotation implements SynthesizedAnnotation {
+
+ private final R root;
+ private final T annotation;
+ private final Map attributeMethodCaches;
+ private final int verticalDistance;
+ private final int horizontalDistance;
+
+ /**
+ * 创建一个合成注解
+ *
+ * @param root 根对象
+ * @param annotation 被合成的注解对象
+ * @param verticalDistance 距离根对象的水平距离
+ * @param horizontalDistance 距离根对象的垂直距离
+ */
+ protected GenericSynthesizedAnnotation(
+ R root, T annotation, int verticalDistance, int horizontalDistance) {
+ this.root = root;
+ this.annotation = annotation;
+ this.verticalDistance = verticalDistance;
+ this.horizontalDistance = horizontalDistance;
+ this.attributeMethodCaches = new HashMap<>();
+ this.attributeMethodCaches.putAll(loadAttributeMethods());
+ }
+
+ /**
+ * 加载注解属性
+ *
+ * @return 注解属性
+ */
+ protected Map loadAttributeMethods() {
+ return Stream.of(ClassUtil.getDeclaredMethods(annotation.annotationType()))
+ .filter(AnnotationUtil::isAttributeMethod)
+ .collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method)));
+ }
+
+ /**
+ * 元注解是否存在该属性
+ *
+ * @param attributeName 属性名
+ * @return 是否存在该属性
+ */
+ public boolean hasAttribute(String attributeName) {
+ return attributeMethodCaches.containsKey(attributeName);
+ }
+
+ /**
+ * 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
+ *
+ * @param attributeName 属性名
+ * @param returnType 返回值类型
+ * @return 是否存在该属性
+ */
+ @Override
+ public boolean hasAttribute(String attributeName, Class> returnType) {
+ return Opt.ofNullable(attributeMethodCaches.get(attributeName))
+ .filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType()))
+ .isPresent();
+ }
+
+ /**
+ * 获取该注解的全部属性
+ *
+ * @return 注解属性
+ */
+ @Override
+ public Map getAttributes() {
+ return this.attributeMethodCaches;
+ }
+
+ /**
+ * 设置属性值
+ *
+ * @param attributeName 属性名称
+ * @param attribute 注解属性
+ */
+ @Override
+ public void setAttribute(String attributeName, AnnotationAttribute attribute) {
+ attributeMethodCaches.put(attributeName, attribute);
+ }
+
+ /**
+ * 替换属性值
+ *
+ * @param attributeName 属性名
+ * @param operator 替换操作
+ */
+ @Override
+ public void replaceAttribute(String attributeName, UnaryOperator operator) {
+ AnnotationAttribute old = attributeMethodCaches.get(attributeName);
+ if (ObjectUtil.isNotNull(old)) {
+ attributeMethodCaches.put(attributeName, operator.apply(old));
+ }
+ }
+
+ /**
+ * 获取属性值
+ *
+ * @param attributeName 属性名
+ * @return 属性值
+ */
+ @Override
+ public Object getAttributeValue(String attributeName) {
+ return Opt.ofNullable(attributeMethodCaches.get(attributeName))
+ .map(AnnotationAttribute::getValue)
+ .get();
+ }
+
+ /**
+ * 获取该合成注解对应的根节点
+ *
+ * @return 合成注解对应的根节点
+ */
+ @Override
+ public R getRoot() {
+ return root;
+ }
+
+ /**
+ * 获取被合成的注解对象
+ *
+ * @return 注解对象
+ */
+ @Override
+ public T getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * 获取该合成注解与根对象的垂直距离。
+ * 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
+ *
+ * @return 合成注解与根对象的垂直距离
+ */
+ @Override
+ public int getVerticalDistance() {
+ return verticalDistance;
+ }
+
+ /**
+ * 获取该合成注解与根对象的水平距离。
+ * 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
+ *
+ * @return 合成注解与根对象的水平距离
+ */
+ @Override
+ public int getHorizontalDistance() {
+ return horizontalDistance;
+ }
+
+ /**
+ * 获取被合成的注解类型
+ *
+ * @return 被合成的注解类型
+ */
+ @Override
+ public Class extends Annotation> annotationType() {
+ return annotation.annotationType();
+ }
+
+ /**
+ * 获取注解属性值
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @return 注解属性值
+ */
+ @Override
+ public Object getAttributeValue(String attributeName, Class> attributeType) {
+ return Opt.ofNullable(attributeMethodCaches.get(attributeName))
+ .filter(method -> ClassUtil.isAssignable(attributeType, method.getAttributeType()))
+ .map(AnnotationAttribute::getValue)
+ .get();
+ }
+}
diff --git a/src/main/java/cn/hutool/core/annotation/Hierarchical.java b/src/main/java/cn/hutool/core/annotation/Hierarchical.java
new file mode 100644
index 0000000..00c9938
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/Hierarchical.java
@@ -0,0 +1,155 @@
+package cn.hutool.core.annotation;
+
+
+import java.util.Comparator;
+
+/**
+ * 描述以一个参照物为对象,存在于该参照物的层级结构中的对象。
+ *
+ *
该对象可通过{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}
+ * 描述其在以参照物为基点的坐标坐标系中的位置。
+ * 在需要对该接口的实现类进行按优先级排序时,距离{@link #getRoot()}对象越近,则该实现类的优先级越高。
+ * 默认提供了{@link #DEFAULT_HIERARCHICAL_COMPARATOR}用于实现该比较规则。
+ * 一般情况下,{@link #getRoot()}返回值相同的对象之间的比较才有意义。
+ *
+ *
此外,还提供了{@link Selector}接口用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象,
+ * 默认提供了四个实现类:
+ *
+ * - {@link Selector#NEAREST_AND_OLDEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回旧对象;
+ * - {@link Selector#NEAREST_AND_NEWEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回新对象;
+ * - {@link Selector#FARTHEST_AND_OLDEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回旧对象;
+ * - {@link Selector#FARTHEST_AND_NEWEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回新对象;
+ *
+ *
+ * @author huangchengxing
+ */
+public interface Hierarchical extends Comparable {
+
+ // ====================== compare ======================
+
+ /**
+ * 默认{@link #getHorizontalDistance()}与{@link #getVerticalDistance()}排序的比较器
+ */
+ Comparator DEFAULT_HIERARCHICAL_COMPARATOR = Comparator
+ .comparing(Hierarchical::getVerticalDistance)
+ .thenComparing(Hierarchical::getHorizontalDistance);
+
+ /**
+ * 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序
+ *
+ * @param o {@link SynthesizedAnnotation}对象
+ * @return 比较值
+ */
+ @Override
+ default int compareTo(Hierarchical o) {
+ return DEFAULT_HIERARCHICAL_COMPARATOR.compare(this, o);
+ }
+
+ // ====================== hierarchical ======================
+
+ /**
+ * 参照物,即坐标为{@code (0, 0)}的对象。
+ * 当对象本身即为参照物时,该方法应当返回其本身
+ *
+ * @return 参照物
+ */
+ Object getRoot();
+
+ /**
+ * 获取该对象与参照物的垂直距离。
+ * 默认情况下,该距离即为当前对象与参照物之间相隔的层级数。
+ *
+ * @return 合成注解与根对象的垂直距离
+ */
+ int getVerticalDistance();
+
+ /**
+ * 获取该对象与参照物的水平距离。
+ * 默认情况下,该距离即为当前对象在与参照物{@link #getVerticalDistance()}相同的情况下条,
+ * 该对象被扫描到的顺序。
+ *
+ * @return 合成注解与根对象的水平距离
+ */
+ int getHorizontalDistance();
+
+ // ====================== selector ======================
+
+ /**
+ * {@link Hierarchical}选择器,用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象
+ */
+ @FunctionalInterface
+ interface Selector {
+
+ /**
+ * 返回距离根对象更近的对象,当距离一样时优先返回旧对象
+ */
+ Selector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
+
+ /**
+ * 返回距离根对象更近的对象,当距离一样时优先返回新对象
+ */
+ Selector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
+
+ /**
+ * 返回距离根对象更远的对象,当距离一样时优先返回旧对象
+ */
+ Selector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
+
+ /**
+ * 返回距离根对象更远的对象,当距离一样时优先返回新对象
+ */
+ Selector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
+
+ /**
+ * 比较两个被合成的对象,选择其中的一个并返回
+ *
+ * @param 复合注解类型
+ * @param prev 上一对象,该参数不允许为空
+ * @param next 下一对象,该参数不允许为空
+ * @return 对象
+ */
+ T choose(T prev, T next);
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回旧注解
+ */
+ class NearestAndOldestPrioritySelector implements Selector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
+ }
+ }
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回新注解
+ */
+ class NearestAndNewestPrioritySelector implements Selector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
+ }
+ }
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回旧注解
+ */
+ class FarthestAndOldestPrioritySelector implements Selector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
+ }
+ }
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回新注解
+ */
+ class FarthestAndNewestPrioritySelector implements Selector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/Link.java b/src/main/java/cn/hutool/core/annotation/Link.java
new file mode 100644
index 0000000..8f4e20f
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/Link.java
@@ -0,0 +1,49 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。
+ * 在通过{@link SynthesizedAggregateAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
+ *
+ *
该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor},
+ * 使用三个子注解等同于{@link Link}。但是需要注意的是,
+ * 当注解中的属性同时存在多个{@link Link}或基于{@link Link}的子注解时,
+ * 仅有声明在被注解的属性最上方的注解会生效,其余注解都将被忽略。
+ *
+ * 注意:该注解的优先级低于{@link Alias}
+ *
+ * @author huangchengxing
+ * @see SynthesizedAggregateAnnotation
+ * @see RelationType
+ * @see AliasFor
+ * @see MirrorFor
+ * @see ForceAliasFor
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface Link {
+
+ /**
+ * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
+ *
+ * @return 关联的注解类型
+ */
+ Class extends Annotation> annotation() default Annotation.class;
+
+ /**
+ * {@link #annotation()}指定注解中关联的属性
+ *
+ * @return 属性名
+ */
+ String attribute() default "";
+
+ /**
+ * {@link #attribute()}指定属性与当前注解的属性建的关联关系类型
+ *
+ * @return 关系类型
+ */
+ RelationType type() default RelationType.MIRROR_FOR;
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/MirrorFor.java b/src/main/java/cn/hutool/core/annotation/MirrorFor.java
new file mode 100644
index 0000000..7d69b34
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/MirrorFor.java
@@ -0,0 +1,42 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ *
{@link Link}的子注解。表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。
+ * 它们遵循下述规则:
+ *
+ * - 互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;
+ * - 互为镜像的两个属性,类型必须一致;
+ * - 互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;
+ * - 互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;
+ *
+ * 注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效
+ *
+ * @author huangchengxing
+ * @see Link
+ * @see RelationType#MIRROR_FOR
+ */
+@Link(type = RelationType.MIRROR_FOR)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface MirrorFor {
+
+ /**
+ * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
+ *
+ * @return 关联的注解类型
+ */
+ @Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
+ Class extends Annotation> annotation() default Annotation.class;
+
+ /**
+ * {@link #annotation()}指定注解中关联的属性
+ *
+ * @return 属性名
+ */
+ @Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
+ String attribute() default "";
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java b/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java
new file mode 100644
index 0000000..1fc0a20
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java
@@ -0,0 +1,132 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+/**
+ * 用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。
+ * 当该处理器执行完毕后,原始合成注解中被{@link Link}注解的属性与{@link Link}注解指向的目标注解的属性,
+ * 都将会被被包装并替换为{@link MirroredAnnotationAttribute}。
+ *
+ * @author huangchengxing
+ * @see RelationType#MIRROR_FOR
+ * @see MirroredAnnotationAttribute
+ */
+public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
+
+ private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.MIRROR_FOR };
+
+ @Override
+ public int order() {
+ return Integer.MIN_VALUE + 1;
+ }
+
+ /**
+ * 该处理器只处理{@link Link#type()}类型为{@link RelationType#MIRROR_FOR}的注解属性
+ *
+ * @return 仅有{@link RelationType#MIRROR_FOR}数组
+ */
+ @Override
+ protected RelationType[] processTypes() {
+ return PROCESSED_RELATION_TYPES;
+ }
+
+ /**
+ * 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象,
+ * 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute}
+ *
+ * @param synthesizer 注解合成器
+ * @param annotation {@code originalAttribute}上的{@link Link}注解对象
+ * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
+ * @param originalAttribute {@code originalAnnotation}上的待处理的属性
+ * @param linkedAnnotation {@link Link}指向的关联注解对象
+ * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
+ */
+ @Override
+ protected void processLinkedAttribute(
+ AnnotationSynthesizer synthesizer, Link annotation,
+ SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
+ SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
+
+ // 镜像属性必然成对出现,因此此处必定存在三种情况:
+ // 1.两属性都不为镜像属性,此时继续进行后续处理;
+ // 2.两属性都为镜像属性,并且指向对方,此时无需后续处理;
+ // 3.两属性仅有任意一属性为镜像属性,此时镜像属性必然未指向当前原始属性,此时应该抛出异常;
+ if (originalAttribute instanceof MirroredAnnotationAttribute
+ || linkedAttribute instanceof MirroredAnnotationAttribute) {
+ checkMirrored(originalAttribute, linkedAttribute);
+ return;
+ }
+
+ // 校验镜像关系
+ checkMirrorRelation(annotation, originalAttribute, linkedAttribute);
+ // 包装这一对镜像属性,并替换原注解中的对应属性
+ final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, linkedAttribute);
+ originalAnnotation.setAttribute(originalAttribute.getAttributeName(), mirroredOriginalAttribute);
+ final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(linkedAttribute, originalAttribute);
+ linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute);
+ }
+
+ /**
+ * 检查映射关系是否正确
+ */
+ private void checkMirrored(AnnotationAttribute original, AnnotationAttribute mirror) {
+ final boolean originalAttributeMirrored = original instanceof MirroredAnnotationAttribute;
+ final boolean mirrorAttributeMirrored = mirror instanceof MirroredAnnotationAttribute;
+
+ // 校验通过
+ final boolean passed = originalAttributeMirrored && mirrorAttributeMirrored
+ && ObjectUtil.equals(((MirroredAnnotationAttribute)original).getLinked(), ((MirroredAnnotationAttribute)mirror).getOriginal());
+ if (passed) {
+ return;
+ }
+
+ // 校验失败,拼装异常信息用于抛出异常
+ String errorMsg;
+ // 原始字段已经跟其他字段形成镜像
+ if (originalAttributeMirrored && !mirrorAttributeMirrored) {
+ errorMsg = CharSequenceUtil.format(
+ "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
+ original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
+ );
+ }
+ // 镜像字段已经跟其他字段形成镜像
+ else if (!originalAttributeMirrored && mirrorAttributeMirrored) {
+ errorMsg = CharSequenceUtil.format(
+ "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
+ mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked()
+ );
+ }
+ // 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况
+ else {
+ errorMsg = CharSequenceUtil.format(
+ "attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]",
+ mirror.getAttribute(), original.getAttribute(),
+ mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(),
+ original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
+ );
+ }
+
+ throw new IllegalArgumentException(errorMsg);
+ }
+
+ /**
+ * 基本校验
+ */
+ private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
+ // 镜像属性必须存在
+ checkLinkedAttributeNotNull(original, mirror, annotation);
+ // 镜像属性返回值必须一致
+ checkAttributeType(original, mirror);
+ // 镜像属性上必须存在对应的注解
+ final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR);
+ Assert.isTrue(
+ ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()),
+ "mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]",
+ mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR
+ );
+ checkLinkedSelf(original, mirror);
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java b/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java
new file mode 100644
index 0000000..6bb8000
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java
@@ -0,0 +1,48 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Assert;
+
+/**
+ * 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理
+ *
+ * @author huangchengxing
+ * @see MirrorLinkAnnotationPostProcessor
+ * @see RelationType#MIRROR_FOR
+ */
+public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
+
+ public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
+ super(origin, linked);
+ }
+
+ @Override
+ public Object getValue() {
+ final boolean originIsDefault = original.isValueEquivalentToDefaultValue();
+ final boolean targetIsDefault = linked.isValueEquivalentToDefaultValue();
+ final Object originValue = original.getValue();
+ final Object targetValue = linked.getValue();
+
+ // 都为默认值,或都为非默认值时,两方法的返回值必须相等
+ if (originIsDefault == targetIsDefault) {
+ Assert.equals(
+ originValue, targetValue,
+ "the values of attributes [{}] and [{}] that mirror each other are different: [{}] <==> [{}]",
+ original.getAttribute(), linked.getAttribute(), originValue, targetValue
+ );
+ return originValue;
+ }
+
+ // 两者有一者不为默认值时,优先返回非默认值
+ return originIsDefault ? targetValue : originValue;
+ }
+
+ /**
+ * 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
+ *
+ * @return 是否
+ */
+ @Override
+ public boolean isValueEquivalentToDefaultValue() {
+ return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue();
+ }
+}
diff --git a/src/main/java/cn/hutool/core/annotation/PropIgnore.java b/src/main/java/cn/hutool/core/annotation/PropIgnore.java
new file mode 100644
index 0000000..cc3f6dc
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/PropIgnore.java
@@ -0,0 +1,21 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 属性忽略注解,使用此注解的字段等会被忽略,主要用于Bean拷贝、Bean转Map等
+ * 此注解应用于字段时,忽略读取和设置属性值,应用于setXXX方法忽略设置值,应用于getXXX忽略读取值
+ *
+ * @author Looly
+ * @since 5.4.2
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
+public @interface PropIgnore {
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/RelationType.java b/src/main/java/cn/hutool/core/annotation/RelationType.java
new file mode 100644
index 0000000..6d4c306
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/RelationType.java
@@ -0,0 +1,50 @@
+package cn.hutool.core.annotation;
+
+/**
+ *
注解属性的关系类型
+ * 若将被{@link Link}注解的属性称为“原始属性”,而在{@link Link}注解中指向的注解属性称为“关联属性”,
+ * 则该枚举用于描述“原始属性”与“关联属性”在{@link SynthesizedAggregateAnnotation}处理过程中的作用关系。
+ * 根据在{@link Link#type()}中指定的关系类型的不同,通过{@link SynthesizedAggregateAnnotation}合成的注解的属性值也将有所变化。
+ *
+ *
当一个注解中的所有属性同时具备多种关系时,将依次按下述顺序处理:
+ *
+ * - 属性上的{@link Alias}注解;
+ * - 属性上的{@link Link}注解,且{@link Link#type()}为{@link #MIRROR_FOR};
+ * - 属性上的{@link Link}注解,且{@link Link#type()}为{@link #FORCE_ALIAS_FOR};
+ * - 属性上的{@link Link}注解,且{@link Link#type()}为{@link #ALIAS_FOR};
+ *
+ *
+ * @author huangchengxing
+ * @see SynthesizedAggregateAnnotation
+ * @see Link
+ */
+public enum RelationType {
+
+ /**
+ * 表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。
+ * 它们遵循下述规则:
+ *
+ * - 互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;
+ * - 互为镜像的两个属性,类型必须一致;
+ * - 互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;
+ * - 互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;
+ *
+ */
+ MIRROR_FOR,
+
+ /**
+ * 表示“原始属性”将作为“关联属性”的别名。
+ *
+ * - 当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;
+ * - 当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;
+ *
+ */
+ ALIAS_FOR,
+
+ /**
+ * 表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
+ * 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
+ */
+ FORCE_ALIAS_FOR
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java
new file mode 100644
index 0000000..e006f3f
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java
@@ -0,0 +1,102 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.Annotation;
+
+/**
+ *
表示基于特定规则聚合,将一组注解聚合而来的注解对象,
+ * 该注解对象允许根据一定规则“合成”一些跟原始注解属性不一样合成注解。
+ *
+ *
合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
+ * 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
+ * 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation},
+ * 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。
+ * {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
+ * 自定义选择器以拦截原始注解被扫描的过程。
+ *
+ *
当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
+ * 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor},
+ * 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
+ * 该钩子一般用于根据{@link Link}注解对属性进行调整。
+ * {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
+ * 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
+ *
+ *
合成注解允许通过{@link #synthesize(Class)}合成一个指定的注解对象,
+ * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成,
+ * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
+ * 处理后的、用于合成当前实例的全部关联注解的相关属性。
+ * {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子,
+ * 自定义属性处理器以拦截合成注解的取值过程。
+ *
+ * @author huangchengxing
+ * @see AnnotationSynthesizer
+ * @see SynthesizedAnnotation
+ * @see SynthesizedAnnotationSelector
+ * @see SynthesizedAnnotationAttributeProcessor
+ * @see SynthesizedAnnotationPostProcessor
+ * @see GenericSynthesizedAggregateAnnotation
+ */
+public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider {
+
+ // ================== hierarchical ==================
+
+ /**
+ * 距离{@link #getRoot()}返回值的垂直距离,
+ * 默认聚合注解即为根对象,因此返回0
+ *
+ * @return 距离{@link #getRoot()}返回值的水平距离,
+ */
+ @Override
+ default int getVerticalDistance() {
+ return 0;
+ }
+
+ /**
+ * 距离{@link #getRoot()}返回值的水平距离,
+ * 默认聚合注解即为根对象,因此返回0
+ *
+ * @return 距离{@link #getRoot()}返回值的水平距离,
+ */
+ @Override
+ default int getHorizontalDistance() {
+ return 0;
+ }
+
+ // ================== synthesize ==================
+
+ /**
+ * 获取在聚合中存在的指定注解对象
+ *
+ * @param annotationType 注解类型
+ * @param 注解类型
+ * @return 注解对象
+ */
+ T getAnnotation(Class annotationType);
+
+ /**
+ * 获取合成注解属性处理器
+ *
+ * @return 合成注解属性处理器
+ */
+ SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor();
+
+ /**
+ * 获取当前的注解类型
+ *
+ * @return 注解类型
+ */
+ @Override
+ default Class extends Annotation> annotationType() {
+ return this.getClass();
+ }
+
+ /**
+ * 从聚合中获取指定类型的属性值
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @return 属性值
+ */
+ @Override
+ Object getAttributeValue(String attributeName, Class> attributeType);
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java
new file mode 100644
index 0000000..4180599
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java
@@ -0,0 +1,96 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.collection.CollUtil;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+
+/**
+ * 用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。
+ * 当对多个合成注解排序时,默认使用{@link #DEFAULT_HIERARCHICAL_COMPARATOR}进行排序,
+ * 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序,
+ * 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。
+ *
+ * @author huangchengxing
+ * @see SynthesizedAggregateAnnotation
+ */
+public interface SynthesizedAnnotation extends Annotation, Hierarchical, AnnotationAttributeValueProvider {
+
+ /**
+ * 获取被合成的注解对象
+ *
+ * @return 注解对象
+ */
+ Annotation getAnnotation();
+
+ /**
+ * 获取该合成注解与根对象的垂直距离。
+ * 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
+ *
+ * @return 合成注解与根对象的垂直距离
+ */
+ @Override
+ int getVerticalDistance();
+
+ /**
+ * 获取该合成注解与根对象的水平距离。
+ * 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
+ *
+ * @return 合成注解与根对象的水平距离
+ */
+ @Override
+ int getHorizontalDistance();
+
+ /**
+ * 注解是否存在该名称相同,且类型一致的属性
+ *
+ * @param attributeName 属性名
+ * @param returnType 返回值类型
+ * @return 是否存在该属性
+ */
+ boolean hasAttribute(String attributeName, Class> returnType);
+
+ /**
+ * 获取该注解的全部属性
+ *
+ * @return 注解属性
+ */
+ Map getAttributes();
+
+ /**
+ * 设置该注解的全部属性
+ *
+ * @param attributes 注解属性
+ */
+ default void setAttributes(Map attributes) {
+ if (CollUtil.isNotEmpty(attributes)) {
+ attributes.forEach(this::setAttribute);
+ }
+ }
+
+ /**
+ * 设置属性值
+ *
+ * @param attributeName 属性名称
+ * @param attribute 注解属性
+ */
+ void setAttribute(String attributeName, AnnotationAttribute attribute);
+
+ /**
+ * 替换属性值
+ *
+ * @param attributeName 属性名
+ * @param operator 替换操作
+ */
+ void replaceAttribute(String attributeName, UnaryOperator operator);
+
+ /**
+ * 获取属性值
+ *
+ * @param attributeName 属性名
+ * @return 属性值
+ */
+ Object getAttributeValue(String attributeName);
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java
new file mode 100644
index 0000000..7a7debd
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java
@@ -0,0 +1,24 @@
+package cn.hutool.core.annotation;
+
+import java.util.Collection;
+
+/**
+ * 合成注解属性选择器。用于在{@link SynthesizedAggregateAnnotation}中从指定类型的合成注解里获取到对应的属性值
+ *
+ * @author huangchengxing
+ */
+@FunctionalInterface
+public interface SynthesizedAnnotationAttributeProcessor {
+
+ /**
+ * 从一批被合成注解中,获取指定名称与类型的属性值
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @param synthesizedAnnotations 被合成的注解
+ * @param 属性类型
+ * @return 属性值
+ */
+ R getAttributeValue(String attributeName, Class attributeType, Collection extends SynthesizedAnnotation> synthesizedAnnotations);
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java
new file mode 100644
index 0000000..91e701b
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java
@@ -0,0 +1,71 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.comparator.CompareUtil;
+
+import java.util.Comparator;
+
+/**
+ * 被合成注解后置处理器,用于在{@link SynthesizedAggregateAnnotation}加载完所有待合成注解后,
+ * 再对加载好的{@link SynthesizedAnnotation}进行后置处理。
+ * 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序,
+ * 该值更小的处理器将被优先执行。
+ *
+ *
该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合:
+ *
+ * - {@link AliasAnnotationPostProcessor};
+ * - {@link MirrorLinkAnnotationPostProcessor};
+ * - {@link AliasLinkAnnotationPostProcessor};
+ * - 其他后置处理器;
+ *
+ *
+ * @author huangchengxing
+ * @see AliasAnnotationPostProcessor
+ * @see MirrorLinkAnnotationPostProcessor
+ * @see AliasLinkAnnotationPostProcessor
+ */
+public interface SynthesizedAnnotationPostProcessor extends Comparable {
+
+ /**
+ * 属性上带有{@link Alias}的注解对象的后置处理器
+ */
+ AliasAnnotationPostProcessor ALIAS_ANNOTATION_POST_PROCESSOR = new AliasAnnotationPostProcessor();
+
+ /**
+ * 属性上带有{@link Link},且与其他注解的属性存在镜像关系的注解对象的后置处理器
+ */
+ MirrorLinkAnnotationPostProcessor MIRROR_LINK_ANNOTATION_POST_PROCESSOR = new MirrorLinkAnnotationPostProcessor();
+
+ /**
+ * 属性上带有{@link Link},且与其他注解的属性存在别名关系的注解对象的后置处理器
+ */
+ AliasLinkAnnotationPostProcessor ALIAS_LINK_ANNOTATION_POST_PROCESSOR = new AliasLinkAnnotationPostProcessor();
+
+ /**
+ * 在一组后置处理器中被调用的顺序,越小越靠前
+ *
+ * @return 排序值
+ */
+ default int order() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * 比较两个后置处理器的{@link #order()}返回值
+ *
+ * @param o 比较对象
+ * @return 大小
+ */
+ @Override
+ default int compareTo(SynthesizedAnnotationPostProcessor o) {
+ return CompareUtil.compare(this, o, Comparator.comparing(SynthesizedAnnotationPostProcessor::order));
+ }
+
+ /**
+ * 给定指定被合成注解与其所属的合成注解聚合器实例,经过处理后返回最终
+ *
+ * @param synthesizedAnnotation 合成的注解
+ * @param synthesizer 注解合成器
+ */
+ void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer);
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java
new file mode 100644
index 0000000..233d849
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java
@@ -0,0 +1,160 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.Opt;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 合成注解代理类,用于为{@link SynthesizedAnnotation}生成对应的合成注解代理对象
+ *
+ * @author huangchengxing
+ * @see SynthesizedAnnotation
+ * @see AnnotationAttributeValueProvider
+ */
+public class SynthesizedAnnotationProxy implements InvocationHandler {
+
+ private final AnnotationAttributeValueProvider annotationAttributeValueProvider;
+ private final SynthesizedAnnotation annotation;
+ private final Map> methods;
+
+ /**
+ * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
+ *
+ * @param 注解类型
+ * @param annotationType 注解类型
+ * @param annotationAttributeValueProvider 注解属性值获取器
+ * @param annotation 合成注解
+ * @return 代理注解
+ */
+ @SuppressWarnings("unchecked")
+ public static T create(
+ Class annotationType,
+ AnnotationAttributeValueProvider annotationAttributeValueProvider,
+ SynthesizedAnnotation annotation) {
+ if (ObjectUtil.isNull(annotation)) {
+ return null;
+ }
+ final SynthesizedAnnotationProxy proxyHandler = new SynthesizedAnnotationProxy(annotationAttributeValueProvider, annotation);
+ if (ObjectUtil.isNull(annotation)) {
+ return null;
+ }
+ return (T) Proxy.newProxyInstance(
+ annotationType.getClassLoader(),
+ new Class[]{annotationType, SyntheticProxyAnnotation.class},
+ proxyHandler
+ );
+ }
+
+ /**
+ * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
+ *
+ * @param 注解类型
+ * @param annotationType 注解类型
+ * @param annotation 合成注解
+ * @return 代理注解
+ */
+ public static T create(
+ Class annotationType, SynthesizedAnnotation annotation) {
+ return create(annotationType, annotation, annotation);
+ }
+
+ /**
+ * 该类是否为通过{@code SynthesizedAnnotationProxy}生成的代理类
+ *
+ * @param annotationType 注解类型
+ * @return 是否
+ */
+ public static boolean isProxyAnnotation(Class> annotationType) {
+ return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, annotationType);
+ }
+
+ SynthesizedAnnotationProxy(AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) {
+ Assert.notNull(annotationAttributeValueProvider, "annotationAttributeValueProvider must not null");
+ Assert.notNull(annotation, "annotation must not null");
+ this.annotationAttributeValueProvider = annotationAttributeValueProvider;
+ this.annotation = annotation;
+ this.methods = new HashMap<>(9);
+ loadMethods();
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return Opt.ofNullable(methods.get(method.getName()))
+ .map(m -> m.apply(method, args))
+ .orElseGet(() -> ReflectUtil.invoke(this, method, args));
+ }
+
+ // ========================= 代理方法 =========================
+
+ void loadMethods() {
+ methods.put("toString", (method, args) -> proxyToString());
+ methods.put("hashCode", (method, args) -> proxyHashCode());
+ methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
+ methods.put("getRoot", (method, args) -> annotation.getRoot());
+ methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
+ methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
+ methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class>) args[1]));
+ methods.put("getAttributes", (method, args) -> annotation.getAttributes());
+ methods.put("setAttribute", (method, args) -> {
+ throw new UnsupportedOperationException("proxied annotation can not reset attributes");
+ });
+ methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
+ methods.put("annotationType", (method, args) -> annotation.annotationType());
+ for (final Method declaredMethod : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) {
+ methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
+ }
+ }
+
+ private String proxyToString() {
+ final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
+ .filter(AnnotationUtil::isAttributeMethod)
+ .map(method -> CharSequenceUtil.format(
+ "{}={}", method.getName(), proxyAttributeValue(method))
+ )
+ .collect(Collectors.joining(", "));
+ return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
+ }
+
+ private int proxyHashCode() {
+ return Objects.hash(annotationAttributeValueProvider, annotation);
+ }
+
+ private Object proxyGetSynthesizedAnnotation() {
+ return annotation;
+ }
+
+ private Object proxyAttributeValue(Method attributeMethod) {
+ return annotationAttributeValueProvider.getAttributeValue(attributeMethod.getName(), attributeMethod.getReturnType());
+ }
+
+ /**
+ * 通过代理类生成的合成注解
+ *
+ * @author huangchengxing
+ */
+ interface SyntheticProxyAnnotation extends SynthesizedAnnotation {
+
+ /**
+ * 获取该代理注解对应的已合成注解
+ *
+ * @return 理注解对应的已合成注解
+ */
+ SynthesizedAnnotation getSynthesizedAnnotation();
+
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java
new file mode 100644
index 0000000..fa2b91e
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java
@@ -0,0 +1,82 @@
+package cn.hutool.core.annotation;
+
+/**
+ * 注解选择器,指定两个注解,选择其中一个返回。
+ * 该接口用于在{@link SynthesizedAggregateAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。
+ *
+ * @author huangchengxing
+ */
+@FunctionalInterface
+public interface SynthesizedAnnotationSelector {
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回旧注解
+ */
+ SynthesizedAnnotationSelector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回新注解
+ */
+ SynthesizedAnnotationSelector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回旧注解
+ */
+ SynthesizedAnnotationSelector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回新注解
+ */
+ SynthesizedAnnotationSelector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
+
+ /**
+ * 比较两个被合成的注解,选择其中的一个并返回
+ *
+ * @param 复合注解类型
+ * @param oldAnnotation 已存在的注解,该参数不允许为空
+ * @param newAnnotation 新获取的注解,该参数不允许为空
+ * @return 被合成的注解
+ */
+ T choose(T oldAnnotation, T newAnnotation);
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回旧注解
+ */
+ class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return Hierarchical.Selector.NEAREST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
+ }
+ }
+
+ /**
+ * 返回距离根对象更近的注解,当距离一样时优先返回新注解
+ */
+ class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return Hierarchical.Selector.NEAREST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
+ }
+ }
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回旧注解
+ */
+ class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return Hierarchical.Selector.FARTHEST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
+ }
+ }
+
+ /**
+ * 返回距离根对象更远的注解,当距离一样时优先返回新注解
+ */
+ class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
+ @Override
+ public T choose(T oldAnnotation, T newAnnotation) {
+ return Hierarchical.Selector.FARTHEST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
+ }
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java b/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java
new file mode 100644
index 0000000..6ca5a3f
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java
@@ -0,0 +1,125 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+/**
+ * 表示一个被包装过的{@link AnnotationAttribute},
+ * 该实例中的一些方法可能会被代理到另一个注解属性对象中,
+ * 从而使得通过原始的注解属性的方法获取到另一注解属性的值。
+ * 除了{@link #getValue()}以外,其他方法的返回值应当尽可能与{@link #getOriginal()}
+ * 返回的{@link AnnotationAttribute}对象的方法返回值一致。
+ *
+ *
当包装类被包装了多层后,则规则生效优先级按包装的先后顺序倒序排序,
+ * 比如a、b互为镜像,此时a、b两属性应当都被{@link MirroredAnnotationAttribute}包装,
+ * 若再指定c为a的别名字段,则c、a、b都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}。
+ * 此时a、b同时被包装了两层,则执行时,优先执行{@link AliasedAnnotationAttribute}的逻辑,
+ * 当该规则不生效时,比如c只有默认值,此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效。
+ *
+ *
被包装的{@link AnnotationAttribute}实际结构为一颗二叉树,
+ * 当包装类再次被包装时,实际上等于又添加了一个新的根节点,
+ * 此时需要同时更新树的全部关联叶子节点。
+ *
+ * @author huangchengxing
+ * @see AnnotationAttribute
+ * @see ForceAliasedAnnotationAttribute
+ * @see AliasedAnnotationAttribute
+ * @see MirroredAnnotationAttribute
+ */
+public interface WrappedAnnotationAttribute extends AnnotationAttribute {
+
+ // =========================== 新增方法 ===========================
+
+ /**
+ * 获取被包装的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
+ *
+ * @return 被包装的{@link AnnotationAttribute}对象
+ */
+ AnnotationAttribute getOriginal();
+
+ /**
+ * 获取最初的被包装的{@link AnnotationAttribute}
+ *
+ * @return 最初的被包装的{@link AnnotationAttribute}
+ */
+ AnnotationAttribute getNonWrappedOriginal();
+
+ /**
+ * 获取包装{@link #getOriginal()}的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
+ *
+ * @return 包装对象
+ */
+ AnnotationAttribute getLinked();
+
+ /**
+ * 遍历以当前实例为根节点的树结构,获取所有未被包装的属性
+ *
+ * @return 叶子节点
+ */
+ Collection getAllLinkedNonWrappedAttributes();
+
+ // =========================== 代理实现 ===========================
+
+ /**
+ * 获取注解对象
+ *
+ * @return 注解对象
+ */
+ @Override
+ default Annotation getAnnotation() {
+ return getOriginal().getAnnotation();
+ }
+
+ /**
+ * 获取注解属性对应的方法
+ *
+ * @return 注解属性对应的方法
+ */
+ @Override
+ default Method getAttribute() {
+ return getOriginal().getAttribute();
+ }
+
+ /**
+ * 该注解属性的值是否等于默认值
+ * 默认仅当{@link #getOriginal()}与{@link #getLinked()}返回的注解属性
+ * 都为默认值时,才返回{@code true}
+ *
+ * @return 该注解属性的值是否等于默认值
+ */
+ @Override
+ boolean isValueEquivalentToDefaultValue();
+
+ /**
+ * 获取属性类型
+ *
+ * @return 属性类型
+ */
+ @Override
+ default Class> getAttributeType() {
+ return getOriginal().getAttributeType();
+ }
+
+ /**
+ * 获取属性上的注解
+ *
+ * @param annotationType 注解类型
+ * @return 注解对象
+ */
+ @Override
+ default T getAnnotation(Class annotationType) {
+ return getOriginal().getAnnotation(annotationType);
+ }
+
+ /**
+ * 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
+ *
+ * @return boolean
+ */
+ @Override
+ default boolean isWrapped() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/package-info.java b/src/main/java/cn/hutool/core/annotation/package-info.java
new file mode 100644
index 0000000..850654b
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 注解包,提供增强型注解和注解工具类
+ *
+ * @author looly
+ *
+ */
+package cn.hutool.core.annotation;
\ No newline at end of file
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java
new file mode 100644
index 0000000..8a3b2e7
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java
@@ -0,0 +1,288 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Proxy;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+/**
+ * 为需要从类的层级结构中获取注解的{@link AnnotationScanner}提供基本实现
+ *
+ * @author huangchengxing
+ */
+public abstract class AbstractTypeAnnotationScanner> implements AnnotationScanner {
+
+ /**
+ * 是否允许扫描父类
+ */
+ private boolean includeSuperClass;
+
+ /**
+ * 是否允许扫描父接口
+ */
+ private boolean includeInterfaces;
+
+ /**
+ * 过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
+ */
+ private Predicate> filter;
+
+ /**
+ * 排除的类型,以上类型及其树结构将直接不被查找
+ */
+ private final Set> excludeTypes;
+
+ /**
+ * 转换器
+ */
+ private final List>> converters;
+
+ /**
+ * 是否有转换器
+ */
+ private boolean hasConverters;
+
+ /**
+ * 当前实例
+ */
+ private final T typedThis;
+
+ /**
+ * 构造一个类注解扫描器
+ *
+ * @param includeSuperClass 是否允许扫描父类
+ * @param includeInterfaces 是否允许扫描父接口
+ * @param filter 过滤器
+ * @param excludeTypes 不包含的类型
+ */
+ @SuppressWarnings("unchecked")
+ protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate> filter, Set> excludeTypes) {
+ Assert.notNull(filter, "filter must not null");
+ Assert.notNull(excludeTypes, "excludeTypes must not null");
+ this.includeSuperClass = includeSuperClass;
+ this.includeInterfaces = includeInterfaces;
+ this.filter = filter;
+ this.excludeTypes = excludeTypes;
+ this.converters = new ArrayList<>();
+ this.typedThis = (T) this;
+ }
+
+ /**
+ * 是否允许扫描父类
+ *
+ * @return 是否允许扫描父类
+ */
+ public boolean isIncludeSuperClass() {
+ return includeSuperClass;
+ }
+
+ /**
+ * 是否允许扫描父接口
+ *
+ * @return 是否允许扫描父接口
+ */
+ public boolean isIncludeInterfaces() {
+ return includeInterfaces;
+ }
+
+ /**
+ * 设置过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
+ *
+ * @param filter 过滤器
+ * @return 当前实例
+ */
+ public T setFilter(Predicate> filter) {
+ Assert.notNull(filter, "filter must not null");
+ this.filter = filter;
+ return typedThis;
+ }
+
+ /**
+ * 添加不扫描的类型,该类型及其树结构将直接不被查找
+ *
+ * @param excludeTypes 不扫描的类型
+ * @return 当前实例
+ */
+ public T addExcludeTypes(Class>... excludeTypes) {
+ CollUtil.addAll(this.excludeTypes, excludeTypes);
+ return typedThis;
+ }
+
+ /**
+ * 添加转换器
+ *
+ * @param converter 转换器
+ * @return 当前实例
+ * @see JdkProxyClassConverter
+ */
+ public T addConverters(UnaryOperator> converter) {
+ Assert.notNull(converter, "converter must not null");
+ this.converters.add(converter);
+ if (!this.hasConverters) {
+ this.hasConverters = CollUtil.isNotEmpty(this.converters);
+ }
+ return typedThis;
+ }
+
+ /**
+ * 是否允许扫描父类
+ *
+ * @param includeSuperClass 是否
+ * @return 当前实例
+ */
+ protected T setIncludeSuperClass(boolean includeSuperClass) {
+ this.includeSuperClass = includeSuperClass;
+ return typedThis;
+ }
+
+ /**
+ * 是否允许扫描父接口
+ *
+ * @param includeInterfaces 是否
+ * @return 当前实例
+ */
+ protected T setIncludeInterfaces(boolean includeInterfaces) {
+ this.includeInterfaces = includeInterfaces;
+ return typedThis;
+ }
+
+ /**
+ * 则根据广度优先递归扫描类的层级结构,并对层级结构中类/接口声明的层级索引和它们声明的注解对象进行处理
+ *
+ * @param consumer 对获取到的注解和注解对应的层级索引的处理
+ * @param annotatedEle 注解元素
+ * @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
+ */
+ @Override
+ public void scan(BiConsumer consumer, AnnotatedElement annotatedEle, Predicate filter) {
+ filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
+ final Class> sourceClass = getClassFormAnnotatedElement(annotatedEle);
+ final Deque>> classDeque = CollUtil.newLinkedList(CollUtil.newArrayList(sourceClass));
+ final Set> accessedTypes = new LinkedHashSet<>();
+ int index = 0;
+ while (!classDeque.isEmpty()) {
+ final List> currClassQueue = classDeque.removeFirst();
+ final List> nextClassQueue = new ArrayList<>();
+ for (Class> targetClass : currClassQueue) {
+ targetClass = convert(targetClass);
+ // 过滤不需要处理的类
+ if (isNotNeedProcess(accessedTypes, targetClass)) {
+ continue;
+ }
+ accessedTypes.add(targetClass);
+ // 扫描父类
+ scanSuperClassIfNecessary(nextClassQueue, targetClass);
+ // 扫描接口
+ scanInterfaceIfNecessary(nextClassQueue, targetClass);
+ // 处理层级索引和注解
+ final Annotation[] targetAnnotations = getAnnotationsFromTargetClass(annotatedEle, index, targetClass);
+ for (final Annotation annotation : targetAnnotations) {
+ if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
+ consumer.accept(index, annotation);
+ }
+ }
+ index++;
+ }
+ if (CollUtil.isNotEmpty(nextClassQueue)) {
+ classDeque.addLast(nextClassQueue);
+ }
+ }
+ }
+
+ /**
+ * 从要搜索的注解元素上获得要递归的类型
+ *
+ * @param annotatedElement 注解元素
+ * @return 要递归的类型
+ */
+ protected abstract Class> getClassFormAnnotatedElement(AnnotatedElement annotatedElement);
+
+ /**
+ * 从类上获取最终所需的目标注解
+ *
+ * @param source 最初的注解元素
+ * @param index 类的层级索引
+ * @param targetClass 类
+ * @return 最终所需的目标注解
+ */
+ protected abstract Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class> targetClass);
+
+ /**
+ * 当前类是否不需要处理
+ *
+ * @param accessedTypes 访问类型
+ * @param targetClass 目标类型
+ * @return 是否不需要处理
+ */
+ protected boolean isNotNeedProcess(Set> accessedTypes, Class> targetClass) {
+ return ObjectUtil.isNull(targetClass)
+ || accessedTypes.contains(targetClass)
+ || excludeTypes.contains(targetClass)
+ || filter.negate().test(targetClass);
+ }
+
+ /**
+ * 若{@link #includeInterfaces}为{@code true},则将目标类的父接口也添加到nextClasses
+ *
+ * @param nextClasses 下一个类集合
+ * @param targetClass 目标类型
+ */
+ protected void scanInterfaceIfNecessary(List> nextClasses, Class> targetClass) {
+ if (includeInterfaces) {
+ final Class>[] interfaces = targetClass.getInterfaces();
+ if (ArrayUtil.isNotEmpty(interfaces)) {
+ CollUtil.addAll(nextClasses, interfaces);
+ }
+ }
+ }
+
+ /**
+ * 若{@link #includeSuperClass}为{@code true},则将目标类的父类也添加到nextClasses
+ *
+ * @param nextClassQueue 下一个类队列
+ * @param targetClass 目标类型
+ */
+ protected void scanSuperClassIfNecessary(List> nextClassQueue, Class> targetClass) {
+ if (includeSuperClass) {
+ final Class> superClass = targetClass.getSuperclass();
+ if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
+ nextClassQueue.add(superClass);
+ }
+ }
+ }
+
+ /**
+ * 若存在转换器,则使用转换器对目标类进行转换
+ *
+ * @param target 目标类
+ * @return 转换后的类
+ */
+ protected Class> convert(Class> target) {
+ if (hasConverters) {
+ for (final UnaryOperator> converter : converters) {
+ target = converter.apply(target);
+ }
+ }
+ return target;
+ }
+
+ /**
+ * 若类型为jdk代理类,则尝试转换为原始被代理类
+ */
+ public static class JdkProxyClassConverter implements UnaryOperator> {
+ @Override
+ public Class> apply(Class> sourceClass) {
+ return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
+ }
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
new file mode 100644
index 0000000..dc108cb
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
@@ -0,0 +1,198 @@
+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;
+
+/**
+ * 注解扫描器,用于从支持的可注解元素上获取所需注解
+ *
+ *
默认提供了以下扫描方式:
+ *
+ * - {@link #NOTHING}:什么都不做,什么注解都不扫描;
+ * - {@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;
+ * -
+ * {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
+ * 以及这些注解的元注解;
+ *
+ * - {@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;
+ * - {@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;
+ * - {@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;
+ * - {@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;
+ * - {@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;
+ * - {@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;
+ *
+ *
+ * @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 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 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 getAnnotations(AnnotatedElement annotatedEle) {
+ final List 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 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 consumer, AnnotatedElement annotatedEle, Predicate 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 consumer, AnnotatedElement annotatedEle, Predicate filter) {
+ if (support(annotatedEle)) {
+ scan(consumer, annotatedEle, filter);
+ }
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java
new file mode 100644
index 0000000..88b22bc
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java
@@ -0,0 +1,44 @@
+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 consumer, AnnotatedElement annotatedEle, Predicate filter) {
+ filter = ObjectUtil.defaultIfNull(filter,a-> t -> true);
+ Stream.of(annotatedEle.getAnnotations())
+ .filter(filter)
+ .forEach(annotation -> consumer.accept(0, annotation));
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java
new file mode 100644
index 0000000..972d1d7
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java
@@ -0,0 +1,31 @@
+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 getAnnotations(AnnotatedElement annotatedEle) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void scan(BiConsumer consumer, AnnotatedElement annotatedEle, Predicate filter) {
+ // do nothing
+ }
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
new file mode 100644
index 0000000..e7d87cd
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
@@ -0,0 +1,47 @@
+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 consumer, AnnotatedElement annotatedEle, Predicate 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);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java
new file mode 100644
index 0000000..fe40f10
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java
@@ -0,0 +1,149 @@
+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;
+
+/**
+ * 通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
+ *
+ *
当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
+ *
+ * -
+ * 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
+ * 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
+ *
+ * -
+ * 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
+ * 扫描器将扫描层级结构中类、接口声明的注解;
+ *
+ * - 当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;
+ *
+ * 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
+ *
+ * @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 consumer, AnnotatedElement annotatedEle, Predicate 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 consumer,
+ AnnotatedElement annotatedEle,
+ Predicate filter) {
+ // 扫描类上注解
+ final ListValueMap 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);
+ })
+ );
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/MetaAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/MetaAnnotationScanner.java
new file mode 100644
index 0000000..a054232
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/MetaAnnotationScanner.java
@@ -0,0 +1,110 @@
+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 getAnnotations(AnnotatedElement annotatedEle) {
+ final List 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 consumer, AnnotatedElement annotatedEle, Predicate filter) {
+ filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
+ Set> accessed = new HashSet<>();
+ final Deque>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class extends Annotation>) annotatedEle));
+ int distance = 0;
+ do {
+ final List> annotationTypes = deque.removeFirst();
+ for (final Class extends Annotation> type : annotationTypes) {
+ final List 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> 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());
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java
new file mode 100644
index 0000000..6e509f2
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java
@@ -0,0 +1,133 @@
+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 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> filter, Set> excludeTypes) {
+ super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
+ }
+
+ /**
+ * 构造一个方法注解扫描器
+ *
+ * @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
+ * @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
+ * @param filter 过滤器
+ * @param excludeTypes 不包含的类型
+ */
+ public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate> filter, Set> 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 (!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());
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java b/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
new file mode 100644
index 0000000..15b6fe5
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
@@ -0,0 +1,105 @@
+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 implements AnnotationScanner {
+
+ /**
+ * 构造一个类注解扫描器
+ *
+ * @param includeSupperClass 是否允许扫描父类
+ * @param includeInterfaces 是否允许扫描父接口
+ * @param filter 过滤器
+ * @param excludeTypes 不包含的类型
+ */
+ public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate> filter, Set> 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> {
+ @Override
+ public Class> apply(Class> sourceClass) {
+ return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
+ }
+ }
+
+}
diff --git a/src/main/java/cn/hutool/core/annotation/scanner/package-info.java b/src/main/java/cn/hutool/core/annotation/scanner/package-info.java
new file mode 100644
index 0000000..3d42d61
--- /dev/null
+++ b/src/main/java/cn/hutool/core/annotation/scanner/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 注解包扫描封装
+ *
+ * @author looly
+ *
+ */
+package cn.hutool.core.annotation.scanner;
diff --git a/src/main/java/cn/hutool/core/bean/BeanDesc.java b/src/main/java/cn/hutool/core/bean/BeanDesc.java
new file mode 100644
index 0000000..ed20f2e
--- /dev/null
+++ b/src/main/java/cn/hutool/core/bean/BeanDesc.java
@@ -0,0 +1,324 @@
+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等相关信息描述
+ * 查找Getter和Setter方法时会:
+ *
+ *
+ * 1. 忽略字段和方法名的大小写
+ * 2. Getter查找getXXX、isXXX、getIsXXX
+ * 3. Setter查找setXXX、setIsXXX
+ * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
+ *
+ *
+ * @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 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 getPropMap(boolean ignoreCase) {
+ return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap;
+ }
+
+ /**
+ * 获取字段属性列表
+ *
+ * @return {@link PropDesc} 列表
+ */
+ public Collection 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
+
+ /**
+ * 初始化
+ * 只有与属性关联的相关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 (!ModifierUtil.isStatic(field) && !ReflectUtil.isOuterClassField(field)) {
+ prop = createProp(field, gettersAndSetters);
+ // 只有不存在时才放入,防止父类属性覆盖子类属性
+ this.propMap.putIfAbsent(prop.getFieldName(), prop);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 根据字段创建属性描述
+ * 查找Getter和Setter方法时会:
+ *
+ *
+ * 1. 忽略字段和方法名的大小写
+ * 2. Getter查找getXXX、isXXX、getIsXXX
+ * 3. Setter查找setXXX、setIsXXX
+ * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
+ *
+ *
+ * @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方法
+ * 匹配规则如下(忽略大小写):
+ *
+ *
+ * 字段名 -》 方法名
+ * isName -》 isName
+ * isName -》 isIsName
+ * isName -》 getIsName
+ * name -》 isName
+ * name -》 getName
+ *
+ *
+ * @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方法
+ * 匹配规则如下(忽略大小写):
+ *
+ *
+ * 字段名 -》 方法名
+ * isName -》 setName
+ * isName -》 setIsName
+ * name -》 setName
+ *
+ *
+ * @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 (!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
+}
diff --git a/src/main/java/cn/hutool/core/bean/BeanDescCache.java b/src/main/java/cn/hutool/core/bean/BeanDescCache.java
new file mode 100644
index 0000000..fa29e1d
--- /dev/null
+++ b/src/main/java/cn/hutool/core/bean/BeanDescCache.java
@@ -0,0 +1,37 @@
+package cn.hutool.core.bean;
+
+import cn.hutool.core.lang.func.Func0;
+import cn.hutool.core.map.WeakConcurrentMap;
+
+/**
+ * Bean属性缓存
+ * 缓存用于防止多次反射造成的性能问题
+ *
+ * @author Looly
+ */
+public enum BeanDescCache {
+ INSTANCE;
+
+ private final WeakConcurrentMap, 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 supplier) {
+ return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
+ }
+
+ /**
+ * 清空全局的Bean属性缓存
+ *
+ * @since 5.7.21
+ */
+ public void clear() {
+ this.bdCache.clear();
+ }
+}
diff --git a/src/main/java/cn/hutool/core/bean/BeanException.java b/src/main/java/cn/hutool/core/bean/BeanException.java
new file mode 100644
index 0000000..227e280
--- /dev/null
+++ b/src/main/java/cn/hutool/core/bean/BeanException.java
@@ -0,0 +1,32 @@
+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);
+ }
+}
diff --git a/src/main/java/cn/hutool/core/bean/BeanPath.java b/src/main/java/cn/hutool/core/bean/BeanPath.java
new file mode 100644
index 0000000..3a7d153
--- /dev/null
+++ b/src/main/java/cn/hutool/core/bean/BeanPath.java
@@ -0,0 +1,324 @@
+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.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象
+ * 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
+ *
+ * - .表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值
+ * - []表达式,可以获取集合等对象中对应index的值
+ *
+ *
+ * 表达式栗子:
+ *
+ *
+ * persion
+ * persion.name
+ * persons[3]
+ * person.friends[5].name
+ * ['person']['friends'][5]['name']
+ *
+ *
+ * @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 patternParts;
+
+ /**
+ * 解析Bean路径表达式为Bean模式
+ * Bean表达式,用于获取多层嵌套Bean中的字段值或Bean对象
+ * 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
+ *
+ * - .表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值
+ * - []表达式,可以获取集合等对象中对应index的值
+ *
+ *
+ * 表达式栗子:
+ *
+ *
+ * persion
+ * persion.name
+ * persons[3]
+ * person.friends[5].name
+ * ['person']['friends'][5]['name']
+ *
+ *
+ * @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 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对应)的值
+ * 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值
+ * 注意:
+ *
+ *
+ * 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
+ * 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
+ *
+ *
+ * @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对应)的值
+ * 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值
+ * 注意:
+ *
+ *
+ * 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
+ * 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
+ *
+ *
+ * @param bean Bean、Map或List
+ * @param patternParts 表达式块列表
+ * @param value 值
+ * @return 值
+ */
+ private void set(Object bean, List patternParts, boolean nextNumberPart, Object value) {
+ Object subBean = this.get(patternParts, bean, true);
+ if (null == subBean) {
+ final List parentParts = getParentParts(patternParts);
+ this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>());
+ //set中有可能做过转换,因此此处重新获取bean
+ subBean = this.get(patternParts, bean, true);
+ }
+ BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
+ }
+
+ /**
+ * 判断path列表中末尾的标记是否为数字
+ *
+ * @param patternParts path列表
+ * @return 是否为数字
+ */
+ private static boolean lastIsNumber(List patternParts) {
+ return NumberUtil.isInteger(patternParts.get(patternParts.size() - 1));
+ }
+
+ /**
+ * 获取父级路径列表
+ *
+ * @param patternParts 路径列表
+ * @return 父级路径列表
+ */
+ private static List getParentParts(List 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 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 && !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 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 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) bean, unWrappedKeys);
+ } else {
+ final Map 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 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 = (!isInWrap);
+ continue;
+ }
+
+ if (!isInWrap && ArrayUtil.contains(EXP_CHARS, c)) {
+ // 处理边界符号
+ if (CharUtil.BRACKET_END == c) {
+ // 中括号(数字下标)结束
+ if (!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
+}
diff --git a/src/main/java/cn/hutool/core/bean/BeanUtil.java b/src/main/java/cn/hutool/core/bean/BeanUtil.java
new file mode 100644
index 0000000..254f871
--- /dev/null
+++ b/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -0,0 +1,917 @@
+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.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ModifierUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * Bean工具类
+ *
+ *
+ * 把一个拥有对属性进行set和get方法的类,我们就可以称之为JavaBean。
+ *
+ *
+ * @author Looly
+ * @since 3.1.2
+ */
+public class BeanUtil {
+
+ /**
+ * 判断是否为可读的Bean对象,判定方法是:
+ *
+ *
+ * 1、是否存在只有无参数的getXXX方法或者isXXX方法
+ * 2、是否存在public类型的字段
+ *
+ *
+ * @param clazz 待测试类
+ * @return 是否为可读的Bean对象
+ * @see #hasGetter(Class)
+ * @see #hasPublicField(Class)
+ */
+ public static boolean isReadableBean(Class> clazz) {
+ return hasGetter(clazz) || hasPublicField(clazz);
+ }
+
+ /**
+ * 判断是否为Bean对象,判定方法是:
+ *
+ *
+ * 1、是否存在只有一个参数的setXXX方法
+ * 2、是否存在public类型的字段
+ *
+ *
+ * @param clazz 待测试类
+ * @return 是否为Bean对象
+ * @see #hasSetter(Class)
+ * @see #hasPublicField(Class)
+ */
+ public static boolean isBean(Class> clazz) {
+ return hasSetter(clazz) || hasPublicField(clazz);
+ }
+
+ /**
+ * 判断是否有Setter方法
+ * 判定方法是否存在只有一个参数的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对象
+ * 判定方法是否存在只有无参数的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) {
+ if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
+ 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) && !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 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
+
+
+ /**
+ * 获得字段值,通过反射直接获得字段值,并不调用getXXX方法
+ * 对象同样支持Map类型,fieldNameOrIndex即为key
+ *
+ *
+ * - Map: fieldNameOrIndex需为key,获取对应value
+ * - Collection: fieldNameOrIndex当为数字,返回index对应值,非数字遍历集合返回子bean对应name值
+ * - Array: fieldNameOrIndex当为数字,返回index对应值,非数字遍历数组返回子bean对应name值
+ *
+ *
+ * @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方法
+ * 对象同样支持Map类型,fieldNameOrIndex即为key
+ *
+ * @param bean Bean
+ * @param fieldNameOrIndex 字段名或序号,序号支持负数
+ * @param value 值
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public static void 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)) {
+ ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
+ } else {
+ // 普通Bean对象
+ ReflectUtil.setFieldValue(bean, fieldNameOrIndex, value);
+ }
+ }
+
+ /**
+ * 解析Bean中的属性值
+ *
+ * @param 属性值类型
+ * @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 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 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 mapToBean(Map, ?> map, Class beanClass, boolean isIgnoreError) {
+ return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
+ }
+
+ /**
+ * Map转换为Bean对象
+ * 忽略大小写
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param beanClass Bean Class
+ * @param isIgnoreError 是否忽略注入错误
+ * @return Bean
+ * @deprecated 请使用 {@link #toBeanIgnoreCase(Object, Class, boolean)}
+ */
+ @Deprecated
+ public static T mapToBeanIgnoreCase(Map, ?> map, Class beanClass, boolean isIgnoreError) {
+ return fillBeanWithMapIgnoreCase(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
+ }
+
+ /**
+ * Map转换为Bean对象
+ *
+ * @param Bean类型
+ * @param map {@link Map}
+ * @param beanClass Bean Class
+ * @param copyOptions 转Bean选项
+ * @return Bean
+ * @deprecated 请使用 {@link #toBean(Object, Class, CopyOptions)}
+ */
+ @Deprecated
+ public static T mapToBean(Map, ?> map, Class beanClass, CopyOptions copyOptions) {
+ return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), copyOptions);
+ }
+
+ /**
+ * Map转换为Bean对象
+ *
+ * @param Bean类型
+ * @param map {@link Map}
+ * @param beanClass Bean Class
+ * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
+ * @param copyOptions 转Bean选项
+ * @return Bean
+ */
+ public static T mapToBean(Map, ?> map, Class beanClass, boolean isToCamelCase, CopyOptions copyOptions) {
+ return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isToCamelCase, copyOptions);
+ }
+
+ // --------------------------------------------------------------------------------------------- fillBeanWithMap
+
+ /**
+ * 使用Map填充Bean对象
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param bean Bean
+ * @param isIgnoreError 是否忽略注入错误
+ * @return Bean
+ */
+ public static T fillBeanWithMap(Map, ?> map, T bean, boolean isIgnoreError) {
+ return fillBeanWithMap(map, bean, false, isIgnoreError);
+ }
+
+ /**
+ * 使用Map填充Bean对象,可配置将下划线转换为驼峰
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param bean Bean
+ * @param isToCamelCase 是否将下划线模式转换为驼峰模式
+ * @param isIgnoreError 是否忽略注入错误
+ * @return Bean
+ */
+ public static T fillBeanWithMap(Map, ?> map, T bean, boolean isToCamelCase, boolean isIgnoreError) {
+ return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.create().setIgnoreError(isIgnoreError));
+ }
+
+ /**
+ * 使用Map填充Bean对象,忽略大小写
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param bean Bean
+ * @param isIgnoreError 是否忽略注入错误
+ * @return Bean
+ */
+ public static T fillBeanWithMapIgnoreCase(Map, ?> map, T bean, boolean isIgnoreError) {
+ return fillBeanWithMap(map, bean, CopyOptions.create().setIgnoreCase(true).setIgnoreError(isIgnoreError));
+ }
+
+ /**
+ * 使用Map填充Bean对象
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param bean Bean
+ * @param copyOptions 属性复制选项 {@link CopyOptions}
+ * @return Bean
+ */
+ public static T fillBeanWithMap(Map, ?> map, T bean, CopyOptions copyOptions) {
+ return fillBeanWithMap(map, bean, false, copyOptions);
+ }
+
+ /**
+ * 使用Map填充Bean对象
+ *
+ * @param Bean类型
+ * @param map Map
+ * @param bean Bean
+ * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
+ * @param copyOptions 属性复制选项 {@link CopyOptions}
+ * @return Bean
+ * @since 3.3.1
+ */
+ public static 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 转换的Bean类型
+ * @param source Bean对象或Map
+ * @param clazz 目标的Bean类型
+ * @return Bean对象
+ * @since 4.1.20
+ */
+ public static T toBean(Object source, Class clazz) {
+ return toBean(source, clazz, null);
+ }
+
+ /**
+ * 对象或Map转Bean,忽略字段转换时发生的异常
+ *
+ * @param 转换的Bean类型
+ * @param source Bean对象或Map
+ * @param clazz 目标的Bean类型
+ * @return Bean对象
+ * @since 5.4.0
+ */
+ public static T toBeanIgnoreError(Object source, Class clazz) {
+ return toBean(source, clazz, CopyOptions.create().setIgnoreError(true));
+ }
+
+ /**
+ * 对象或Map转Bean,忽略字段转换时发生的异常
+ *
+ * @param 转换的Bean类型
+ * @param source Bean对象或Map
+ * @param clazz 目标的Bean类型
+ * @param ignoreError 是否忽略注入错误
+ * @return Bean对象
+ * @since 5.4.0
+ */
+ public static