惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CXSECURITY Database RSS Feed - CXSecurity.com
Help Net Security
Help Net Security
P
Privacy International News Feed
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
A
Arctic Wolf
Latest news
Latest news
T
Threat Research - Cisco Blogs
L
LINUX DO - 最新话题
P
Privacy & Cybersecurity Law Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Google DeepMind News
Google DeepMind News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
月光博客
月光博客
N
News and Events Feed by Topic
Jina AI
Jina AI
博客园 - 司徒正美
WordPress大学
WordPress大学
罗磊的独立博客
雷峰网
雷峰网
AI
AI
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Security @ Cisco Blogs
博客园 - 三生石上(FineUI控件)
H
Heimdal Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cisco Blogs
博客园 - 【当耐特】
The Hacker News
The Hacker News
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
博客园 - Franky
S
SegmentFault 最新的问题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Cloudbric
Cloudbric
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
Last Week in AI
Last Week in AI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Know Your Adversary
Know Your Adversary
Google DeepMind News
Google DeepMind News

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
浅谈组合注解 & 注解别名 | 青空之蓝
Otstar Lin · 2021-01-15 · via 青空之蓝

浅谈组合注解 & 注解别名

前言

这篇文章很早就躺在了草稿里了,一直没有写 2333,最近在考试,因为都是一些相对简单的考试,同时又暂停的项目的开发,所以最近相对较闲,便打算把这个坑填一下。

什么是组合注解和注解别名?

如果你看过 Spring 的注解的源码,那么这两个概念一定不会陌生。

注解别名 指的注解的属性拥有别名的功能,让多个属性值表达同一个意思,如 Spring 的 @Bean 注解:

public @interface Bean {
	@AliasFor("name")
	String[] value() default {};

	@AliasFor("value")
	String[] name() default {};
}
public @interface Bean {
	@AliasFor("name")
	String[] value() default {};

	@AliasFor("value")
	String[] name() default {};
}

从代码中看到 value 属性和 name 属性是相同的,设置 valuename 任意一个值都代表了设置了 Bean 的名称。这就是注解别名。

组合注解 简单来说就是 Spring 自行实现的将多个注解组合到一个注解上的功能。如 Spring 里的 @RestController 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
	@AliasFor(annotation = Controller.class)
	String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
	@AliasFor(annotation = Controller.class)
	String value() default "";
}

从代码中看到,@RestController 注解上标记了 @Controller@ResponseBody 注解,这样 @RestController 就组合了 @Controller@ResponseBody 的功能。

除了 组合注解注解别名,Spring 还提供了类似于类继承的 注解继承 功能,比如 @RestControllervalue 属性上标记了 @AliasFor(annotation = Controller.class),此时若设置了 @RestContollervalue 属性,则代表设置了 @Contollervalue 属性。

Spring 是如何实现的?

首先我们先随便写一个 Demo:

@Controller
public class CityController {

    @GetMapping("/city")
    public String index(Model model) {
        model.addAttribute("provinces", CityConst.getProvinces());
        return "city/index";
    }
}
@Controller
public class CityController {

    @GetMapping("/city")
    public String index(Model model) {
        model.addAttribute("provinces", CityConst.getProvinces());
        return "city/index";
    }
}
@SpringBootTest
@Slf4j
class ApplicationTests {

    @Test
    void contextLoads() throws NoSuchMethodException {
        final RequestMapping annotation = AnnotationUtils.getAnnotation(
            CityController.class.getMethod("index", Model.class),
            RequestMapping.class
        );
        annotation.path();
    }
}
@SpringBootTest
@Slf4j
class ApplicationTests {

    @Test
    void contextLoads() throws NoSuchMethodException {
        final RequestMapping annotation = AnnotationUtils.getAnnotation(
            CityController.class.getMethod("index", Model.class),
            RequestMapping.class
        );
        annotation.path();
    }
}

启动调试会话,一路步入就可以看到以下的代码段:

final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnnotation<A> {

    // ...

    static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
		Assert.notNull(annotation, "Annotation must not be null");
		AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
		return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
	}

	// ...
}
final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnnotation<A> {

    // ...

    static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
		Assert.notNull(annotation, "Annotation must not be null");
		AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
		return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
	}

	// ...
}

可以看到,Spring 在创建 MergedAnnotation 前会先获取 AnnotationTypeMappings,该对象保存了 MergedAnnotation.from 传入的注解及其所有父注解(非 JDK 注解)的 AnnotationTypeMapping 信息,AnnotationTypeMapping 里保存了根注解(Spring 保存的方式是自下向上的,所以这个的根注解是 from 传入的注解),源注解(下一级注解,如 RequestMapping 的源注解是 GetMapping),别名索引数组,别名指向的方法等信息。然后获取当前传入注解的 AnnotationTypeMapping 组成 MergedAnnotation

不过有了 MergedAnnotation 那么如何把 MergedAnnotation 变成 Java 的注解呢?如果直接返回通过反射获取的注解,那么别名和子注解的值传上来的值就无法被更改,所以为了获得 Java 的注解,Spring 会重新创建该注解的注解代理类。

如果你看过 Java 注解的源码,Java 注解其实是通过 JDK 代理实现的,通过 JDK 代理从 AnnotationInvocationHandler 里的 memberValues Map 获取注解值。Spring 就是使用类似的方式通过 MergedAnnotation.synthesize 方法调用了 SynthesizedMergedAnnotationInvocationHandler.createProxy 动态创建了 Java 注解,而 SynthesizedMergedAnnotationInvocationHandler 包装了 MergedAnnotationAttributeMethods

final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> implements InvocationHandler {

    private final MergedAnnotation<?> annotation;
	private final AttributeMethods attributes;

    // ...

    static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation, Class<A> type) {
		ClassLoader classLoader = type.getClassLoader();
		InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(annotation, type);
		Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class) ?
				new Class<?>[] {type, SynthesizedAnnotation.class} : new Class<?>[] {type};
		return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
	}

	// ...
}
final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> implements InvocationHandler {

    private final MergedAnnotation<?> annotation;
	private final AttributeMethods attributes;

    // ...

    static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation, Class<A> type) {
		ClassLoader classLoader = type.getClassLoader();
		InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(annotation, type);
		Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class) ?
				new Class<?>[] {type, SynthesizedAnnotation.class} : new Class<?>[] {type};
		return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
	}

	// ...
}

到此 Spring 处理组合注解的原理的关键部分就差不多讲完了,至于获取值部分就不说明了,只是简单的取到属性对应的方法,然后利用反射获取值。

实现

既然知道了原理,那么就可以自行实现一个组合注解 & 注解别名了。

方式

在进行编码前我们要先确定我们组合注解的实现方式。Spring 的实现方式较为复杂,所以我们不采用 Spring 的实现方法,而是直接对 Java 注解里的 memberValues Map 动手,通过修改这个 Map 的值,我们就可以修改注解的属性值。不过需要注意,Java 注解是单例的,所以我们不能直接修改从反射获取的注解里的 memberValues,而是要克隆一份,另外使用 JDK 动态代理创建注解对象。

代码

首先我们先准备 @AliasFor 别名注解:

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AliasFor {
    String value() default "";

    Class<? extends Annotation> annotation() default Annotation.class;

    String attribute() default "";
}
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AliasFor {
    String value() default "";

    Class<? extends Annotation> annotation() default Annotation.class;

    String attribute() default "";
}

为了方便实现重复注解我添加了一个 @RepeatItem 注解:

@Target({ ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatItem {
    Class<? extends Annotation> value();
}
@Target({ ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatItem {
    Class<? extends Annotation> value();
}

然后就是注解工具类的一些基础方法:

public class AnnotationUtils {
    @SuppressWarnings("unchecked")
    private static Map<String, Object> getMemberValues(
        final Annotation annotation
    ) {
        try {
            final InvocationHandler invocationHandler = Proxy.getInvocationHandler(
                annotation
            );
            final Field field = invocationHandler
                .getClass()
                .getDeclaredField("memberValues");
            field.setAccessible(true);
            return (Map<String, Object>) field.get(invocationHandler);
        } catch (final Exception e) {
            throw new RuntimeException("Get annotation member values failed");
        }
    }

    public static boolean isDefaultValue(
        final Method method,
        final Map<String, Object> memberValues
    ) {
        return isDefaultValue(method, memberValues.get(method.getName()));
    }

    public static boolean isDefaultValue(
        final Method method,
        final Object value
    ) {
        final Object defaultValue = method.getDefaultValue();
        if (method.getReturnType().isArray()) {
            return Arrays.equals((Object[]) defaultValue, (Object[]) value);
        } else {
            return defaultValue.equals(value);
        }
    }

    public static boolean isJdkAnnotation(
        final Class<? extends Annotation> type
    ) {
        return (
            type == Documented.class ||
            type == Retention.class ||
            type == Inherited.class ||
            type == Native.class ||
            type == Repeatable.class ||
            type == Target.class
        );
    }

    public static Class<? extends Annotation> getRepeatItem(
        final Class<? extends Annotation> annotationType
    ) {
        final RepeatItem repeatItem = annotationType.getAnnotation(
            RepeatItem.class
        );
        if (repeatItem == null) {
            return null;
        }
        return repeatItem.value();
    }

    public static Class<? extends Annotation> getRepeatable(
        final Class<? extends Annotation> annotationType
    ) {
        final Repeatable repeatable = annotationType.getAnnotation(
            Repeatable.class
        );
        if (repeatable == null) {
            return null;
        }
        return repeatable.value();
    }
}
public class AnnotationUtils {
    @SuppressWarnings("unchecked")
    private static Map<String, Object> getMemberValues(
        final Annotation annotation
    ) {
        try {
            final InvocationHandler invocationHandler = Proxy.getInvocationHandler(
                annotation
            );
            final Field field = invocationHandler
                .getClass()
                .getDeclaredField("memberValues");
            field.setAccessible(true);
            return (Map<String, Object>) field.get(invocationHandler);
        } catch (final Exception e) {
            throw new RuntimeException("Get annotation member values failed");
        }
    }

    public static boolean isDefaultValue(
        final Method method,
        final Map<String, Object> memberValues
    ) {
        return isDefaultValue(method, memberValues.get(method.getName()));
    }

    public static boolean isDefaultValue(
        final Method method,
        final Object value
    ) {
        final Object defaultValue = method.getDefaultValue();
        if (method.getReturnType().isArray()) {
            return Arrays.equals((Object[]) defaultValue, (Object[]) value);
        } else {
            return defaultValue.equals(value);
        }
    }

    public static boolean isJdkAnnotation(
        final Class<? extends Annotation> type
    ) {
        return (
            type == Documented.class ||
            type == Retention.class ||
            type == Inherited.class ||
            type == Native.class ||
            type == Repeatable.class ||
            type == Target.class
        );
    }

    public static Class<? extends Annotation> getRepeatItem(
        final Class<? extends Annotation> annotationType
    ) {
        final RepeatItem repeatItem = annotationType.getAnnotation(
            RepeatItem.class
        );
        if (repeatItem == null) {
            return null;
        }
        return repeatItem.value();
    }

    public static Class<? extends Annotation> getRepeatable(
        final Class<? extends Annotation> annotationType
    ) {
        final Repeatable repeatable = annotationType.getAnnotation(
            Repeatable.class
        );
        if (repeatable == null) {
            return null;
        }
        return repeatable.value();
    }
}

为了创建注解代理类,我们还需要一个 InvocationHandler 用于代理属性方法,我懒得写了就直接把 JDK 里的 AnnotationInvocationHandler 拷贝了出来(JDK 里的是私有的不方便使用),由于代码太长了,这里就不贴了,可以到 Github 上查看。

然后就是相应的代理工具方法:

public class AnnotationUtils {
    @SuppressWarnings("unchecked")
    public static <A extends Annotation> A annotationForMap(
        final Class<A> annotationType,
        final Map<String, Object> memberValues
    ) {
        return (A) Proxy.newProxyInstance(
            annotationType.getClassLoader(),
            new Class[] { annotationType },
            new AnnotationInvocationHandler(annotationType, memberValues)
        );
    }
}
public class AnnotationUtils {
    @SuppressWarnings("unchecked")
    public static <A extends Annotation> A annotationForMap(
        final Class<A> annotationType,
        final Map<String, Object> memberValues
    ) {
        return (A) Proxy.newProxyInstance(
            annotationType.getClassLoader(),
            new Class[] { annotationType },
            new AnnotationInvocationHandler(annotationType, memberValues)
        );
    }
}

然后就是最关键的合并注解值的方法了:

public class AnnotationUtils {
    private static Map<String, Object> mergeAnnotationValue(
        final Annotation annotation,
        final Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
    ) {
        final Class<? extends Annotation> annotationType = annotation.annotationType();
        final Map<String, Object> overwrite = overwriteMap.get(annotationType);
        // 原始 memberValues,不可修改,因为这是单例的
        final Map<String, Object> memberValues = getMemberValues(annotation);
        // 实际复制并操作后的 memberValues
        final Map<String, Object> values = new HashMap<>(memberValues.size());
        for (Entry<String, Object> entry : memberValues.entrySet()) {
            final String attributeName = entry.getKey();
            Object attributeValue = entry.getValue();
            final Method method = ReflectUtil.getMethod(
                annotationType,
                attributeName
            );
            final AliasFor aliasFor = method.getAnnotation(AliasFor.class);
            if (overwrite != null && overwrite.containsKey(attributeName)) {
                // 如果从子元素设置了重写的值,那么就设置该值
                attributeValue = overwrite.get(attributeName);
            } else if (
                aliasFor != null &&
                !aliasFor.value().isEmpty() &&
                isDefaultValue(method, memberValues)
            ) {
                // 如果为默认值,同时设置了 AliasFor.value 那么就使用别名的值(即使是默认值也一样)
                final String alias = aliasFor.value();
                if (overwrite != null && overwrite.containsKey(alias)) {
                    attributeValue = overwrite.get(alias);
                } else {
                    attributeValue = memberValues.get(alias);
                }
            }
            // 否则把自身 memberValues 值设置到新 Map 中
            values.put(attributeName, attributeValue);
            // 如果设置了 AliasFor.annotation 那么就设置父注解的重写值
            if (aliasFor != null && aliasFor.annotation() != Annotation.class) {
                final Class<? extends Annotation> parentType = aliasFor.annotation();
                final Map<String, Object> parentOverwrite = overwriteMap.getOrDefault(
                    parentType,
                    new HashMap<>()
                );
                parentOverwrite.put(
                    aliasFor.attribute().isEmpty()
                        ? attributeName
                        : aliasFor.attribute(),
                    attributeValue
                );
                overwriteMap.put(parentType, parentOverwrite);
            }
        }
        return values;
    }
}
public class AnnotationUtils {
    private static Map<String, Object> mergeAnnotationValue(
        final Annotation annotation,
        final Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
    ) {
        final Class<? extends Annotation> annotationType = annotation.annotationType();
        final Map<String, Object> overwrite = overwriteMap.get(annotationType);
        // 原始 memberValues,不可修改,因为这是单例的
        final Map<String, Object> memberValues = getMemberValues(annotation);
        // 实际复制并操作后的 memberValues
        final Map<String, Object> values = new HashMap<>(memberValues.size());
        for (Entry<String, Object> entry : memberValues.entrySet()) {
            final String attributeName = entry.getKey();
            Object attributeValue = entry.getValue();
            final Method method = ReflectUtil.getMethod(
                annotationType,
                attributeName
            );
            final AliasFor aliasFor = method.getAnnotation(AliasFor.class);
            if (overwrite != null && overwrite.containsKey(attributeName)) {
                // 如果从子元素设置了重写的值,那么就设置该值
                attributeValue = overwrite.get(attributeName);
            } else if (
                aliasFor != null &&
                !aliasFor.value().isEmpty() &&
                isDefaultValue(method, memberValues)
            ) {
                // 如果为默认值,同时设置了 AliasFor.value 那么就使用别名的值(即使是默认值也一样)
                final String alias = aliasFor.value();
                if (overwrite != null && overwrite.containsKey(alias)) {
                    attributeValue = overwrite.get(alias);
                } else {
                    attributeValue = memberValues.get(alias);
                }
            }
            // 否则把自身 memberValues 值设置到新 Map 中
            values.put(attributeName, attributeValue);
            // 如果设置了 AliasFor.annotation 那么就设置父注解的重写值
            if (aliasFor != null && aliasFor.annotation() != Annotation.class) {
                final Class<? extends Annotation> parentType = aliasFor.annotation();
                final Map<String, Object> parentOverwrite = overwriteMap.getOrDefault(
                    parentType,
                    new HashMap<>()
                );
                parentOverwrite.put(
                    aliasFor.attribute().isEmpty()
                        ? attributeName
                        : aliasFor.attribute(),
                    attributeValue
                );
                overwriteMap.put(parentType, parentOverwrite);
            }
        }
        return values;
    }
}

梳理下流程会更方便阅读:

首先我们要明确一点,注解的处理顺序是先子注解,然后父注解。

  1. 获取原始 memberValues 的 Map
  2. 创建克隆的空 memberValues
  3. 循环遍历原始 memberValues,取出每一项的值
  4. 通过属性名称从注解 Class 查找 Method,然后取得 @AliasFor 的注解
  5. 如果子注解有传上来覆盖的值,那么就使用这个值(overwriteMap 里存放)
  6. 否则看下有没有设置 @AliasFor 注解及 value 值(别名)如果设置了,则判断是否是默认值(Method 可以获取方法默认值,也就是属性默认值),如果不是默认值,那么这个值就是被设置过的,此时就不应该覆盖它。
  7. 如果设置了 @AliasFor 注解同时不是默认值,那么就获取别名的值(注意这个别名的值也有可能是子注解传上来的,所以需要判断下 overwriteMap 里有没有设置)
  8. 最后将获得的最终的值存入克隆的 memberValues
  9. 然后处理要传到父注解的值,如果设置了 @AliasForannotation 值,那么就将这个值设置到对应的 overwriteMap 里,这样父注解在处理的时候就可以获取到这个值了

有了处理注解值的方法,那么就可以写遍历注解的方法了:

public class AnnotationUtils {
    private static void walkAnnotation(
        final AnnotatedElement element,
        Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap,
        Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
    ) {
        for (Annotation annotation : element.getAnnotations()) {
            final Class<? extends Annotation> annotationType = annotation.annotationType();
            if (isJdkAnnotation(annotationType)) {
                continue;
            }
            // 处理当前注解
            final List<Map<String, Object>> annotations = annotationMap.getOrDefault(
                annotationType,
                new ArrayList<>()
            );
            for (Annotation item : element.getAnnotationsByType(
                annotationType
            )) {
                annotations.add(mergeAnnotationValue(item, overwriteMap));
            }
            annotationMap.put(annotationType, annotations);
            // 处理重复注解
            final Class<? extends Annotation> repeatItem = getRepeatItem(
                annotationType
            );
            if (repeatItem != null) {
                final List<Map<String, Object>> itemList = annotationMap.getOrDefault(
                    repeatItem,
                    new ArrayList<>()
                );
                for (final Annotation item : (Annotation[]) ReflectUtil.invoke(
                    annotation,
                    "value"
                )) {
                    itemList.add(mergeAnnotationValue(item, overwriteMap));
                }
                annotationMap.put(repeatItem, itemList);
            }
            // 处理父注解
            walkAnnotation(annotationType, annotationMap, overwriteMap);
        }
    }
}
public class AnnotationUtils {
    private static void walkAnnotation(
        final AnnotatedElement element,
        Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap,
        Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
    ) {
        for (Annotation annotation : element.getAnnotations()) {
            final Class<? extends Annotation> annotationType = annotation.annotationType();
            if (isJdkAnnotation(annotationType)) {
                continue;
            }
            // 处理当前注解
            final List<Map<String, Object>> annotations = annotationMap.getOrDefault(
                annotationType,
                new ArrayList<>()
            );
            for (Annotation item : element.getAnnotationsByType(
                annotationType
            )) {
                annotations.add(mergeAnnotationValue(item, overwriteMap));
            }
            annotationMap.put(annotationType, annotations);
            // 处理重复注解
            final Class<? extends Annotation> repeatItem = getRepeatItem(
                annotationType
            );
            if (repeatItem != null) {
                final List<Map<String, Object>> itemList = annotationMap.getOrDefault(
                    repeatItem,
                    new ArrayList<>()
                );
                for (final Annotation item : (Annotation[]) ReflectUtil.invoke(
                    annotation,
                    "value"
                )) {
                    itemList.add(mergeAnnotationValue(item, overwriteMap));
                }
                annotationMap.put(repeatItem, itemList);
            }
            // 处理父注解
            walkAnnotation(annotationType, annotationMap, overwriteMap);
        }
    }
}

照样写个流程吧:

  1. 因为传入的是可被标注的元素,所以就取出这个元素所标注的所有注解,遍历
  2. 如果是 JDK 注解就直接跳过,因为这些注解对我们的程序无用,而且会导致无限递归
  3. 接着就是使用 AnnotatedElementgetAnnotationsByType 方法获取标注在元素上的所有指定注解类型的值。之所以要这么做是因为 Java 支持重复注解,通过 getAnnotations 的方法只能取得一个同类型的注解
  4. 取得注解后就调用 mergeAnnotationValue 方法合并注解值,同时把要传到父注解的值存入 overwriteMap
  5. 除了 Java 标准的重复注解,我们还常用带 s 的注解包裹来做到重复注解,如 @MapperScans@MapperScan 此时就需要特殊处理,这里使用的方法是在带 s 的注解里添加一个 @RepeatItem 注解,然后该注解存储单个注解的类型,这样就能把带 s 注解里的单个注解存到它对应类型的注解里。
  6. 处理完当前注解后就接着处理父注解(递归处理)

遍历完所有的注解后,就取得了可标注元素的所有注解处理过的 memberValues,此时就可以将 memberValues 转成注解了。

public class AnnotationUtils {
    private static Map<Class<? extends Annotation>, List<Annotation>> annotationForMergeAnnotation(
        Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap
    ) {
        return annotationMap
            .entrySet()
            .stream()
            .collect(
                Collectors.toMap(
                    Entry::getKey,
                    e ->
                        e
                            .getValue()
                            .stream()
                            .map(
                                i ->
                                    AnnotationUtils.annotationForMap(
                                        e.getKey(),
                                        i
                                    )
                            )
                            .collect(Collectors.toList()),
                    (u, v) -> {
                        throw new IllegalStateException(
                            String.format("Duplicate key %s", u)
                        );
                    },
                    LinkedHashMap::new
                )
            );
    }

    public static Map<Class<? extends Annotation>, List<Annotation>> mergeAnnotation(
        final AnnotatedElement element
    ) {
        return MERGED_ANNOTATION_CACHE.computeIfAbsent(
            element,
            k -> {
                final Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap = new LinkedHashMap<>();
                walkAnnotation(k, annotationMap, new HashMap<>());
                return annotationForMergeAnnotation(annotationMap);
            }
        );
    }
}
public class AnnotationUtils {
    private static Map<Class<? extends Annotation>, List<Annotation>> annotationForMergeAnnotation(
        Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap
    ) {
        return annotationMap
            .entrySet()
            .stream()
            .collect(
                Collectors.toMap(
                    Entry::getKey,
                    e ->
                        e
                            .getValue()
                            .stream()
                            .map(
                                i ->
                                    AnnotationUtils.annotationForMap(
                                        e.getKey(),
                                        i
                                    )
                            )
                            .collect(Collectors.toList()),
                    (u, v) -> {
                        throw new IllegalStateException(
                            String.format("Duplicate key %s", u)
                        );
                    },
                    LinkedHashMap::new
                )
            );
    }

    public static Map<Class<? extends Annotation>, List<Annotation>> mergeAnnotation(
        final AnnotatedElement element
    ) {
        return MERGED_ANNOTATION_CACHE.computeIfAbsent(
            element,
            k -> {
                final Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap = new LinkedHashMap<>();
                walkAnnotation(k, annotationMap, new HashMap<>());
                return annotationForMergeAnnotation(annotationMap);
            }
        );
    }
}

此时就有了标注在可标注元素上所有的注解了,有了这些就可以封装成组合注解了:

首先是 MergedAnnotation 的接口:

public interface MergedAnnotation {
    String VALUE = "value";
    Logger log = LoggerFactory.getLogger(MergedAnnotation.class);

    /**
     * 获取所有注解
     *
     * @return 注解 Map
     */
    Map<Class<? extends Annotation>, List<Annotation>> annotations();

    /**
     * 获取注解
     *
     * @param annotationType 注解类型
     * @param <A>            注解类型
     * @return 注解
     */
    default <A extends Annotation> A getAnnotation(
        final Class<A> annotationType
    ) {
        return this.getAnnotation(annotationType, 0);
    }

    /**
     * 获取注解
     *
     * @param annotationType 注解类型
     * @param index          索引
     * @param <A>            注解类型
     * @return 注解
     */
    @SuppressWarnings("unchecked")
    default <A extends Annotation> A getAnnotation(
        final Class<A> annotationType,
        final int index
    ) {
        final List<Annotation> annotations =
            this.annotations().get(annotationType);
        if (
            annotations == null ||
            annotations.isEmpty() ||
            index < 0 ||
            index >= annotations.size()
        ) {
            return null;
        }
        if (annotations.size() > 1) {
            log.warn(
                "Annotation [{}] is multi, but only get one",
                annotationType.getName()
            );
        }
        return (A) annotations.get(index);
    }

    /**
     * 获取指定类型的注解列表
     *
     * @param annotationType 注解类型
     * @param <A>            注解类型
     * @return 注解列表
     */
    @SuppressWarnings("unchecked")
    default <A extends Annotation> List<A> getAnnotations(
        Class<A> annotationType
    ) {
        return (List<A>) this.annotations()
            .getOrDefault(annotationType, Collections.emptyList());
    }

    /**
     * 是否存在注解
     *
     * @param annotationType 注解类型
     * @return 是否存在
     */
    default boolean hasAnnotation(
        final Class<? extends Annotation> annotationType
    ) {
        return this.annotations().containsKey(annotationType);
    }

    /**
     * 是否不存在注解
     *
     * @param annotationType 注解类型
     * @return 是否不存在
     */
    default boolean notAnnotation(Class<? extends Annotation> annotationType) {
        return !this.hasAnnotation(annotationType);
    }

    /**
     * 是否包含多个相同类型的注解
     *
     * @param annotationType 注解类型
     * @return 是否包含
     */
    default boolean hasMultiAnnotation(
        final Class<? extends Annotation> annotationType
    ) {
        return (
            this.annotations().containsKey(annotationType) &&
            this.annotations().get(annotationType).size() != 1
        );
    }

    /**
     * 添加注解
     *
     * @param annotation 注解
     */
    default void addAnnotation(Annotation annotation) {
        throw new UnsupportedOperationException(
            "Unsupported add annotation to merge annotation"
        );
    }

    /**
     * 删除注解
     *
     * @param annotationType 注解类型
     */
    default void removeAnnotation(Class<? extends Annotation> annotationType) {
        throw new UnsupportedOperationException(
            "Unsupported remove annotation to merge annotation"
        );
    }

    /**
     * 删除注解
     *
     * @param annotationType 注解类型
     * @param index          索引
     */
    default void removeAnnotation(
        Class<? extends Annotation> annotationType,
        int index
    ) {
        throw new UnsupportedOperationException(
            "Unsupported add annotation to merge annotation"
        );
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @return 注解值
     */
    default Object get(Class<? extends Annotation> annotationType) {
        return this.get(annotationType, VALUE);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @return 注解值
     */
    default Object get(
        Class<? extends Annotation> annotationType,
        String name
    ) {
        return this.get(annotationType, name, Object.class);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        Class<T> returnType
    ) {
        return this.get(annotationType, VALUE, returnType);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @param returnType     返回类型
     * @param <T>            注解值类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        String name,
        Class<T> returnType
    ) {
        return this.get(annotationType, name, returnType, 0);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @param returnType     返回类型
     * @param index          索引
     * @param <T>            注解值类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        String name,
        Class<T> returnType,
        int index
    ) {
        final Annotation annotation = this.getAnnotation(annotationType, index);
        final Method method = ReflectUtil.getMethodByName(annotationType, name);
        if (annotation == null || method == null) {
            return null;
        }
        return Convert.convert(
            returnType,
            ReflectUtil.invoke(annotation, method)
        );
    }

    static MergedAnnotation from(AnnotatedElement element) {
        return new MergedAnnotationImpl(element);
    }

    static boolean has(
        AnnotatedElement element,
        Class<? extends Annotation> annotationType
    ) {
        return from(element).getAnnotation(annotationType) != null;
    }
}
public interface MergedAnnotation {
    String VALUE = "value";
    Logger log = LoggerFactory.getLogger(MergedAnnotation.class);

    /**
     * 获取所有注解
     *
     * @return 注解 Map
     */
    Map<Class<? extends Annotation>, List<Annotation>> annotations();

    /**
     * 获取注解
     *
     * @param annotationType 注解类型
     * @param <A>            注解类型
     * @return 注解
     */
    default <A extends Annotation> A getAnnotation(
        final Class<A> annotationType
    ) {
        return this.getAnnotation(annotationType, 0);
    }

    /**
     * 获取注解
     *
     * @param annotationType 注解类型
     * @param index          索引
     * @param <A>            注解类型
     * @return 注解
     */
    @SuppressWarnings("unchecked")
    default <A extends Annotation> A getAnnotation(
        final Class<A> annotationType,
        final int index
    ) {
        final List<Annotation> annotations =
            this.annotations().get(annotationType);
        if (
            annotations == null ||
            annotations.isEmpty() ||
            index < 0 ||
            index >= annotations.size()
        ) {
            return null;
        }
        if (annotations.size() > 1) {
            log.warn(
                "Annotation [{}] is multi, but only get one",
                annotationType.getName()
            );
        }
        return (A) annotations.get(index);
    }

    /**
     * 获取指定类型的注解列表
     *
     * @param annotationType 注解类型
     * @param <A>            注解类型
     * @return 注解列表
     */
    @SuppressWarnings("unchecked")
    default <A extends Annotation> List<A> getAnnotations(
        Class<A> annotationType
    ) {
        return (List<A>) this.annotations()
            .getOrDefault(annotationType, Collections.emptyList());
    }

    /**
     * 是否存在注解
     *
     * @param annotationType 注解类型
     * @return 是否存在
     */
    default boolean hasAnnotation(
        final Class<? extends Annotation> annotationType
    ) {
        return this.annotations().containsKey(annotationType);
    }

    /**
     * 是否不存在注解
     *
     * @param annotationType 注解类型
     * @return 是否不存在
     */
    default boolean notAnnotation(Class<? extends Annotation> annotationType) {
        return !this.hasAnnotation(annotationType);
    }

    /**
     * 是否包含多个相同类型的注解
     *
     * @param annotationType 注解类型
     * @return 是否包含
     */
    default boolean hasMultiAnnotation(
        final Class<? extends Annotation> annotationType
    ) {
        return (
            this.annotations().containsKey(annotationType) &&
            this.annotations().get(annotationType).size() != 1
        );
    }

    /**
     * 添加注解
     *
     * @param annotation 注解
     */
    default void addAnnotation(Annotation annotation) {
        throw new UnsupportedOperationException(
            "Unsupported add annotation to merge annotation"
        );
    }

    /**
     * 删除注解
     *
     * @param annotationType 注解类型
     */
    default void removeAnnotation(Class<? extends Annotation> annotationType) {
        throw new UnsupportedOperationException(
            "Unsupported remove annotation to merge annotation"
        );
    }

    /**
     * 删除注解
     *
     * @param annotationType 注解类型
     * @param index          索引
     */
    default void removeAnnotation(
        Class<? extends Annotation> annotationType,
        int index
    ) {
        throw new UnsupportedOperationException(
            "Unsupported add annotation to merge annotation"
        );
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @return 注解值
     */
    default Object get(Class<? extends Annotation> annotationType) {
        return this.get(annotationType, VALUE);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @return 注解值
     */
    default Object get(
        Class<? extends Annotation> annotationType,
        String name
    ) {
        return this.get(annotationType, name, Object.class);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        Class<T> returnType
    ) {
        return this.get(annotationType, VALUE, returnType);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @param returnType     返回类型
     * @param <T>            注解值类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        String name,
        Class<T> returnType
    ) {
        return this.get(annotationType, name, returnType, 0);
    }

    /**
     * 获取注解值
     *
     * @param annotationType 注解类型
     * @param name           方法名称
     * @param returnType     返回类型
     * @param index          索引
     * @param <T>            注解值类型
     * @return 注解值
     */
    default <T> T get(
        Class<? extends Annotation> annotationType,
        String name,
        Class<T> returnType,
        int index
    ) {
        final Annotation annotation = this.getAnnotation(annotationType, index);
        final Method method = ReflectUtil.getMethodByName(annotationType, name);
        if (annotation == null || method == null) {
            return null;
        }
        return Convert.convert(
            returnType,
            ReflectUtil.invoke(annotation, method)
        );
    }

    static MergedAnnotation from(AnnotatedElement element) {
        return new MergedAnnotationImpl(element);
    }

    static boolean has(
        AnnotatedElement element,
        Class<? extends Annotation> annotationType
    ) {
        return from(element).getAnnotation(annotationType) != null;
    }
}

然后就是实现类:

public class MergedAnnotationImpl implements MergedAnnotation {

    private final Map<Class<? extends Annotation>, List<Annotation>> annotations;

    public MergedAnnotationImpl(final AnnotatedElement element) {
        this.annotations = AnnotationUtils.mergeAnnotation(element);
    }

    @Override
    public Map<Class<? extends Annotation>, List<Annotation>> annotations() {
        return this.annotations;
    }
}
public class MergedAnnotationImpl implements MergedAnnotation {

    private final Map<Class<? extends Annotation>, List<Annotation>> annotations;

    public MergedAnnotationImpl(final AnnotatedElement element) {
        this.annotations = AnnotationUtils.mergeAnnotation(element);
    }

    @Override
    public Map<Class<? extends Annotation>, List<Annotation>> annotations() {
        return this.annotations;
    }
}

到这里我们自己的组合注解就实现完成了,让我们来使用下吧:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Parent(name = "123")
public @interface Children1 {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Parent(name = "123")
public @interface Children1 {
}
@Children1
class MergedAnnotationTest {

    @Test
    void from() {
        final MergedAnnotation annotation = MergedAnnotation.from(
            MergedAnnotationTest.class
        );
        final Parent parent = annotation.getAnnotation(Parent.class);
        assertEquals("123", parent.name());
        assertEquals("123", parent.value());
    }
}
@Children1
class MergedAnnotationTest {

    @Test
    void from() {
        final MergedAnnotation annotation = MergedAnnotation.from(
            MergedAnnotationTest.class
        );
        final Parent parent = annotation.getAnnotation(Parent.class);
        assertEquals("123", parent.name());
        assertEquals("123", parent.value());
    }
}

结语

组合注解简单化注解配置,用很少的注解来标注特定含义的多个元注解,同时提供了很好的扩展性,可以根据实际需要灵活的自定义注解。经过组合注解的重构后,我们就不再需要写很多注解处理器,避免了重复,同时也不易出错。

浅谈组合注解 & 注解别名

https://blog.ixk.me/post/talking-about-merged-annotation
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2021/01/15

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

浅谈并发:三大特性[青空之蓝-2020]-迷茫