新增注解扫描器和合成注解

This commit is contained in:
Looly 2022-06-27 20:25:14 +08:00
parent 44503270bf
commit a6d4e96a41
8 changed files with 56 additions and 48 deletions

View File

@ -17,6 +17,7 @@
* 【core 】 BeanPath在空元素时默认加入map修改根据下标类型赋值List or mapissue#2362@Github * 【core 】 BeanPath在空元素时默认加入map修改根据下标类型赋值List or mapissue#2362@Github
* 【core 】 localAddressList 添加重构方法pr#665@Gitee * 【core 】 localAddressList 添加重构方法pr#665@Gitee
* 【cron 】 从配置文件加载任务时自定义ID避免重复从配置文件加载issue#I5E7BM@Gitee * 【cron 】 从配置文件加载任务时自定义ID避免重复从配置文件加载issue#I5E7BM@Gitee
* 【core 】 新增注解扫描器和合成注解pr#654@Gitee
* *
### 🐞Bug修复 ### 🐞Bug修复
* 【extra 】 修复createExtractor中抛出异常后流未关闭问题pr#2384@Github * 【extra 】 修复createExtractor中抛出异常后流未关闭问题pr#2384@Github

View File

@ -48,7 +48,7 @@ public class AnnotationUtil {
); );
/** /**
* 是否为Jdk自带的元注解<br /> * 是否为Jdk自带的元注解<br>
* 包括 * 包括
* <ul> * <ul>
* <li>{@link Target}</li> * <li>{@link Target}</li>
@ -63,12 +63,12 @@ public class AnnotationUtil {
* @param annotationType 注解类型 * @param annotationType 注解类型
* @return 是否为Jdk自带的元注解 * @return 是否为Jdk自带的元注解
*/ */
public static boolean isJdkMateAnnotation(Class<? extends Annotation> annotationType) { public static boolean isJdkMetaAnnotation(Class<? extends Annotation> annotationType) {
return META_ANNOTATIONS.contains(annotationType); return META_ANNOTATIONS.contains(annotationType);
} }
/** /**
* 是否不为Jdk自带的元注解<br /> * 是否不为Jdk自带的元注解<br>
* 包括 * 包括
* <ul> * <ul>
* <li>{@link Target}</li> * <li>{@link Target}</li>
@ -84,7 +84,7 @@ public class AnnotationUtil {
* @return 是否为Jdk自带的元注解 * @return 是否为Jdk自带的元注解
*/ */
public static boolean isNotJdkMateAnnotation(Class<? extends Annotation> annotationType) { public static boolean isNotJdkMateAnnotation(Class<? extends Annotation> annotationType) {
return !isJdkMateAnnotation(annotationType); return false == isJdkMetaAnnotation(annotationType);
} }
/** /**
@ -383,7 +383,7 @@ public class AnnotationUtil {
*/ */
public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedElement, Class<T> annotationType) { public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedElement, Class<T> annotationType) {
AnnotationScanner[] scanners = new AnnotationScanner[] { AnnotationScanner[] scanners = new AnnotationScanner[] {
new MateAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
}; };
return AnnotationScanner.scanByAnySupported(annotatedElement, scanners).stream() return AnnotationScanner.scanByAnySupported(annotatedElement, scanners).stream()
.map(SyntheticAnnotation::of) .map(SyntheticAnnotation::of)

View File

@ -1,6 +1,6 @@
package cn.hutool.core.annotation; package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.MateAnnotationScanner; import cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ClassUtil;
@ -23,22 +23,22 @@ import java.util.stream.Stream;
/** /**
* 表示一个根注解与根注解上的多层元注解合成的注解 * 表示一个根注解与根注解上的多层元注解合成的注解
* *
* <p>假设现有注解AA上存在元注解BB上存在元注解C则对A解析得到的合成注解X则CBA都是X的元注解X为根注解<br /> * <p>假设现有注解AA上存在元注解BB上存在元注解C则对A解析得到的合成注解X则CBA都是X的元注解X为根注解<br>
* 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解即是否为当前实例的父类 * 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解即是否为当前实例的父类
* 若指定注解是当前实例的元注解则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例<br /> * 若指定注解是当前实例的元注解则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例<br>
* 需要注意的是由于认为合并注解X以最初的根注解A作为元注解因此{@link #getAnnotations()}{@link #getDeclaredAnnotations()} * 需要注意的是由于认为合并注解X以最初的根注解A作为元注解因此{@link #getAnnotations()}{@link #getDeclaredAnnotations()}
* 都将只能获得A * 都将只能获得A
* *
* <p>不同层级的元注解可能存在同名的属性此时若认为该合成注解X在第0层则根注解A在第1层B在第2层......以此类推 * <p>不同层级的元注解可能存在同名的属性此时若认为该合成注解X在第0层则根注解A在第1层B在第2层......以此类推
* 层级越低的注解中离根注解距离近则属性优先级越高即遵循就近原则<br /> * 层级越低的注解中离根注解距离近则属性优先级越高即遵循就近原则<br>
* 举个例子若CBA同时存在属性y则将X视为CB或者A时获得的y属性的值都与最底层元注解A的值保持一致 * 举个例子若CBA同时存在属性y则将X视为CB或者A时获得的y属性的值都与最底层元注解A的值保持一致
* 同理不同层级可能会出现相同的元注解比如A注解存在元注解BC但是C又存在元注解B因此根据就近原则A上的元注解B将优先于C上的元注解B生效 * 同理不同层级可能会出现相同的元注解比如A注解存在元注解BC但是C又存在元注解B因此根据就近原则A上的元注解B将优先于C上的元注解B生效
* 若两相同注解处于同一层级则按照从其上一级子注解{@link AnnotatedElement#getAnnotations()}的调用顺序排序<br /> * 若两相同注解处于同一层级则按照从其上一级子注解{@link AnnotatedElement#getAnnotations()}的调用顺序排序<br>
* {@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则 * {@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则
* *
* <p>同名属性将根据类型彼此隔离即当不同层级的元注解存在同名的属性但是属性类型不同时此时低层级的属性并不会覆盖高层级注解的属性 * <p>同名属性将根据类型彼此隔离即当不同层级的元注解存在同名的属性但是属性类型不同时此时低层级的属性并不会覆盖高层级注解的属性
* *
* <p>别名在合成注解中仍然有效若注解X中任意属性上存在{@link Alias}注解{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值<br /> * <p>别名在合成注解中仍然有效若注解X中任意属性上存在{@link Alias}注解{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值<br>
* {@link Alias}注解仅能指定注解X中存在的属性作为别名不允许指定元注解或子类注解的属性 * {@link Alias}注解仅能指定注解X中存在的属性作为别名不允许指定元注解或子类注解的属性
* *
* @author huangchengxing * @author huangchengxing
@ -179,7 +179,7 @@ public class SyntheticAnnotation<A extends Annotation> implements Annotation, An
} }
// 扫描元注解 // 扫描元注解
metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, 0)); metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, 0));
new MateAnnotationScanner().scan( new MetaAnnotationScanner().scan(
(index, annotation) -> metaAnnotationMap.computeIfAbsent( (index, annotation) -> metaAnnotationMap.computeIfAbsent(
// 当出现重复的注解时由于后添加的注解必然层级更高优先级更低因此当直接忽略 // 当出现重复的注解时由于后添加的注解必然层级更高优先级更低因此当直接忽略
annotation.annotationType(), t -> new MetaAnnotation(annotation, index) annotation.annotationType(), t -> new MetaAnnotation(annotation, index)

View File

@ -18,7 +18,7 @@ import java.util.stream.Stream;
* @see TypeAnnotationScanner * @see TypeAnnotationScanner
* @see MethodAnnotationScanner * @see MethodAnnotationScanner
* @see FieldAnnotationScanner * @see FieldAnnotationScanner
* @see MateAnnotationScanner * @see MetaAnnotationScanner
*/ */
public interface AnnotationScanner { public interface AnnotationScanner {

View File

@ -17,13 +17,13 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* 扫描注解类上存在的注解支持处理枚举实例或枚举类型 <br /> * 扫描注解类上存在的注解支持处理枚举实例或枚举类型
* 需要注意当待解析是枚举类时有可能与{@link TypeAnnotationScanner}冲突 * 需要注意当待解析是枚举类时有可能与{@link TypeAnnotationScanner}冲突
* *
* @author huangchengxing * @author huangchengxing
* @see TypeAnnotationScanner * @see TypeAnnotationScanner
*/ */
public class MateAnnotationScanner implements AnnotationScanner { public class MetaAnnotationScanner implements AnnotationScanner {
/** /**
* 获取当前注解的元注解后是否继续递归扫描的元注解的元注解 * 获取当前注解的元注解后是否继续递归扫描的元注解的元注解
@ -35,14 +35,14 @@ public class MateAnnotationScanner implements AnnotationScanner {
* *
* @param includeSupperMetaAnnotation 获取当前注解的元注解后是否继续递归扫描的元注解的元注解 * @param includeSupperMetaAnnotation 获取当前注解的元注解后是否继续递归扫描的元注解的元注解
*/ */
public MateAnnotationScanner(boolean includeSupperMetaAnnotation) { public MetaAnnotationScanner(boolean includeSupperMetaAnnotation) {
this.includeSupperMetaAnnotation = includeSupperMetaAnnotation; this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
} }
/** /**
* 构造一个元注解扫描器默认在扫描当前注解上的元注解后并继续递归扫描元注解 * 构造一个元注解扫描器默认在扫描当前注解上的元注解后并继续递归扫描元注解
*/ */
public MateAnnotationScanner() { public MetaAnnotationScanner() {
this(true); this(true);
} }
@ -68,7 +68,7 @@ public class MateAnnotationScanner implements AnnotationScanner {
List<Class<? extends Annotation>> annotationTypes = deque.removeFirst(); List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
for (Class<? extends Annotation> type : annotationTypes) { for (Class<? extends Annotation> type : annotationTypes) {
List<Annotation> metaAnnotations = Stream.of(type.getAnnotations()) List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
.filter(a -> !AnnotationUtil.isJdkMateAnnotation(a.annotationType())) .filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.filter(filter) .filter(filter)
.collect(Collectors.toList()); .collect(Collectors.toList());
for (Annotation metaAnnotation : metaAnnotations) { for (Annotation metaAnnotation : metaAnnotations) {

View File

@ -164,7 +164,7 @@ public class TypeAnnotationScanner implements AnnotationScanner {
return scan((Class<?>)annotatedElement).stream() return scan((Class<?>)annotatedElement).stream()
.map(Class::getAnnotations) .map(Class::getAnnotations)
.flatMap(Stream::of) .flatMap(Stream::of)
.filter(a -> !AnnotationUtil.isJdkMateAnnotation(a.annotationType())) .filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@ -0,0 +1,7 @@
/**
* 注解包扫描封装
*
* @author looly
*
*/
package cn.hutool.core.annotation.scanner;

View File

@ -16,7 +16,7 @@ public class MateAnnotationScannerTest {
@Test @Test
public void testMateAnnotationScanner() { public void testMateAnnotationScanner() {
AnnotationScanner scanner = new MateAnnotationScanner(); AnnotationScanner scanner = new MetaAnnotationScanner();
Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class)); Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
Map<Class<? extends Annotation>, Annotation> annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType); Map<Class<? extends Annotation>, Annotation> annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
Assert.assertEquals(3, annotations.size()); Assert.assertEquals(3, annotations.size());
@ -25,7 +25,7 @@ public class MateAnnotationScannerTest {
Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class)); Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class));
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class)); Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
scanner = new MateAnnotationScanner(false); scanner = new MetaAnnotationScanner(false);
Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class)); Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType); annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
Assert.assertEquals(1, annotations.size()); Assert.assertEquals(1, annotations.size());