spring-core:理解@AliasFor注解的作用

news/2024/9/24 11:23:29/

@AliasForspring注解体系中一个非常重要且基础的注解。顾名思义,它的基本作用就是为注解字段定义一个别名。

基本作用:字段别名

java">	@Testpublic void test3AliasFor() {try {{CasbanScan casbanScan = AnnotationUtils.findAnnotation(UserSummy.class, CasbanScan.class);System.out.println(casbanScan);}} catch (Throwable e) {e.printStackTrace();fail();}}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface CasbanScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};}@CasbanScan("hello")public static class UserSummy{}public static class VipSummy extends UserSummy{}

输出

@CasbanScan(basePackages=[hello], value=[hello])

上面的输出可以看到虽然UserSummy类上的@CasbanScan注解只定义了value字段但通过AnnotationUtils.findAnnotation方法获取到的CasbanScan实例basePackages字段也是有与value字段相同的值,这就是@AliasFor注解的基本作用。

更多:注解继承

除了为注解字段定义别名之外,@AliasFor注解更重要的作用是为注解增加了类似Java普通类的继承功能,如下是@AliasFor的定义源码:

java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {/*** Alias for {@link #attribute}.* <p>Intended to be used instead of {@link #attribute} when {@link #annotation}* is not declared &mdash; for example: {@code @AliasFor("value")} instead of* {@code @AliasFor(attribute = "value")}.*/@AliasFor("attribute")String value() default "";/*** The name of the attribute that <em>this</em> attribute is an alias for.* @see #value*/@AliasFor("value")String attribute() default "";/*** The type of annotation in which the aliased {@link #attribute} is declared.* <p>Defaults to {@link Annotation}, implying that the aliased attribute is* declared in the same annotation as <em>this</em> attribute.*/Class<? extends Annotation> annotation() default Annotation.class;}

上面源码可以看到@AliasFor不仅有value字段还有annotation字段,这个字段的字面作用是声明别名属性的所属的注解类型。
怎么理解呢?
我们知道注解(Annotation)类虽然也是一个接口(@interface),但它与Java的标准接口(interface)是不太一样的,它没有继承能力,
如下普通的Java interface类可以从另一个接口类继承(extends),但@interface不行

java">public interface Request{}
public interface WebRequest extends Request{}

这在一定程序上限制了注解的应用场景。

spring-core的注解体系通过@AliasFor注解为注解设计提供了继承能力。这个继承能力就体现在了@AliasForannotation字段。

如果不定义annotation字段
@AliasForvalueattribute字段用于指定@AliasFor所在字段为同当前注解类内的字段别名。如下例子:value上定义的@AliasFor("basePackages")即指定valuebasePackages字段的别名,反之亦然。

java">	public @interface CasbanScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};}

如果定义了annotation字段
annotation字段可以理解为指定当前字段为定义在当前注解上的注解类指定字段的别名。

如下示例:
@RequestComponent注解上定义了@ScanConfig注解,@RequestComponent注解的subClassFirstly字段上定义了@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")
即定义该字段为@ScanConfigsubClassFirstly的别名

java">@Retention(RetentionPolicy.RUNTIME)
public @interface ScanConfig {String subClassFirstly() default "";
}@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@ScanConfig
public @interface RequestComponent {@AliasFor("associated")String value() default "";@AliasFor("value")String associated() default "";@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")String subClassFirstly() default "";
}

我们可以再定义一个注解@WebComponent也使定义一个firstly字段使用同样的@AliasFor注解,注意这个字段firstly字段与@RequestComponentsubClassFirstly并不同名

java">@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@ScanConfig
public @interface WebComponent {String value() default "";@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")String firstly() default "";
}

展现继承能力

如下示例,调用AnnotatedElementUtils.findMergedAnnotation方法获取类上定义的@ScanConfig

java">	@Testpublic void test4AliasFor() {try {{ScanConfig scanConfig= AnnotatedElementUtils.findMergedAnnotation(UseRequestComponent.class, ScanConfig.class);System.out.printf("ScanConfig:%s in %s\n",UseRequestComponent.class.getSimpleName(), AnnotationUtils.getAnnotationAttributes(scanConfig));assertEquals("hello", scanConfig.subClassFirstly());}{ScanConfig scanConfig = AnnotatedElementUtils.findMergedAnnotation(UseWebComponent.class, ScanConfig.class);System.out.printf("ScanConfig:%s in %s\n",UseWebComponent.class.getSimpleName(), AnnotationUtils.getAnnotationAttributes(scanConfig));assertEquals("world", scanConfig.subClassFirstly());}} catch (Throwable e) {e.printStackTrace();fail();}}@RequestComponent(subClassFirstly="hello")public static class UseRequestComponent{}@WebComponent(firstly = "world")public static class UseWebComponent{}

输出

ScanConfig:UseRequestComponent in {subClassFirstly=hello}
ScanConfig:UseWebComponent in {subClassFirstly=world}

如上示例,我们可以从定义了注解@RequestComponent(subClassFirstly="hello")UseRequestComponent类直接获取@ScanConfig注解值
也可以从定义了注解@WebComponent(firstly = "world")UseWebComponent类直接获取@ScanConfig注解值
虽然@RequestComponent@WebComponent是两个不同的注解类,但因为它们都有@ScanConfig注解。所以AnnotatedElementUtils.findMergedAnnotation方法可以从定义了这两个注解的类上读取它们共同定义的@ScanConfig注解,
从这点来看是不是可以视为@RequestComponent@WebComponent都继承了将@ScanConfig注解呢?

所以@AliasFor注解可以为不同的注解提供共同的字段,类似于父类字段。有了这个能力,基于spring-core注解框架设计自己的注解体系就提供了很大的灵活性和便利性。


http://www.ppmy.cn/news/1435049.html

相关文章

如何修改WordPress数据库表前缀以提高安全性

WordPress作为世界上最受欢迎的内容管理系统之一&#xff0c;吸引了数以百万计的用户。然而&#xff0c;正因为其广泛的使用&#xff0c;WordPress网站也成为了黑客攻击的目标之一。其中一个最常见的安全漏洞是使用默认的数据库表前缀wp_&#xff0c;使得黑客能够更轻松地进行大…

面向对象六大设计原则

1. 单一职责原则&#xff0c;一个合理的类&#xff0c;应该仅有一个引起它变化的原因&#xff0c;即单一职责&#xff0c;就是设计的这个类功能应该只有一个&#xff1b; 优点&#xff1a;消除耦合&#xff0c;减小因需求变化引起代码僵化。 2. 开-闭原则&#xff0c;讲的…

卡尔曼滤波器(二):Simulink卡尔曼滤波器模块使用

观看MATLAB技术讲座笔记&#xff0c;该技术讲座视频来自bilibili账号&#xff1a;MATLAB中国。 本节在Simulink中用卡尔曼滤波器来滤除传感器噪声&#xff0c;准确估算单摆摆角。 一、单摆模型简介 不考虑摩擦时&#xff0c;下图所示的单摆力学平衡方程为&#xff1a; m l 2…

TypeScript基础入门(一、常用类型)

目录 第一章 前言 1.1 介绍TypeScript 1.2 TypeScript相比Js的优势 1.3 使用TypeScript的准备工作 第二章 TypeScript的数据类型 2.1 TypeScript的常用类型 2.1.1 概述 2.1.2 TS使用JS基本数据类型 2.1.2.1 number 2.1.2.2 string 2.1.3.3 boolean 2.1.2.4 null 2…

借鉴行业巨头,构建策划知识体系,助你成为下一个营销天才

策划是一门理论与实践结合的学问&#xff0c;而策划人需要将理论和实践有机的结合起来&#xff0c;才能实现策划理论知识与实际操作相结合的目的。 很多策划人并不会系统的学习策划知识体系&#xff0c;只是将学到的理论知识碎片化的了解一下&#xff0c;如果没有完整的理论体…

数据结构与算法-砖墙问题

砖墙问题 你的面前有一堵矩形的、由 n 行砖块组成的砖墙。这些砖块高度相同&#xff08;也就是一个单位高&#xff09;但是宽度不同。每一行砖块的宽度之和相等。 你现在要画一条 自顶向下 的、穿过 最少 砖块的垂线。如果你画的线只是从砖块的边缘经过&#xff0c;就不算穿过…

使用 hiredis 客户端库封装一个简单的 Redis 类

目录 思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。 封装 Redis 的 C 类的过程可以分为以下几个步骤&#xff1a; 一个完成发布订阅功能的 Redis 类 思考一下redis编程的整个过程。 我们作为redis客户端。需要跟redis服务器交互。 那说白了…

QTableView获取可见的行数

场景 当我们需要实时刷新QTableView时&#xff0c;而此时tableView的数据量较大&#xff0c;如果全部刷新显然不合理&#xff0c;如果可以只对用户看的到的数据进行刷新那就最好了&#xff0c;经过一番摸索找到了几种方式&#xff0c;可供参考 代码 方法1 QVector<int>…