关于Annotation的那些事儿

article/2025/10/14 12:44:27

文章目录

            • 注解的基础方法
            • 元注解与复合注解
            • Repeatable annotation 可重复注解
            • Spring中解析Annotation的工具类


###### 注解的定义说明 对于一个注解一般包括以下几个部分: 1. Target:适用目标 有一个注解如下所示: ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface Component {
/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/
String value() default "";

}

可以看到,使用的目标为类上面,如下所示
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200820144803800.png#pic_center)如果我们尝试使用到方法上,编译器都会报错的。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020082014463424.png#pic_center)
报错信息为:```'@Component' not applicable to method```关于有那些适用范围可以使用,查看类```java.lang.annotation.ElementType```即可。2. Retention : 保留时机
一般情况下都是```java.lang.annotation.RetentionPolicy#RUNTIME```,也就是说这个注解可以一直保持到JVM中(运行时),而```java.lang.annotation.RetentionPolicy#SOURCE```在编译之后就会被丢弃,而```java.lang.annotation.RetentionPolicy#CLASS```不会保留到运行时。
比如:
```java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {}
@SessionScope
@RestController("sessionScopedUserService")
public class SessionScopedUserService extends AbstractUserService {// ...
}

其中RestController@Retention(RetentionPolicy.RUNTIME)
通过java.lang.Class#getDeclaredAnnotations查找类上的注解,结果如下所示,可以看到SessionScope是查找不到的,因为它在编译完就被丢弃了,在JVM加载这个类的时候根本就不存在了。
在这里插入图片描述
修改执行时机:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {}

再次执行,效果如下所示,可以查询到了。
在这里插入图片描述
3. Documented:与javadoc有关,此处不讨论
4. Inherited : 查找的时候是否从父类中查找
比如有以下两个注解

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {String value() default "";}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/String value() default "";}

其中Qualifier中包含了@Inherited元注解。而Component不包含。假如存在以下的类关系图。SessionScopedUserService继承自AbstractUserService,而AbstractUserService包含以上两个注解。
在这里插入图片描述
SessionScopedUserService查找以上两个注解如下所示:
在这里插入图片描述
从结果可以看出,可以查找到注解Qualifier,而无法查找到Component,因为前者包含了元注解@Inherited,在执行getDeclaredAnnotations方法的时候会遍历所有的父类去查找对应的注解。
对应的源码java.lang.Class#createAnnotationData如下所示

if (superClass != null) {// 查找出父类中的注解Map<Class<? extends Annotation>, Annotation> superAnnotations =superClass.annotationData().annotations;for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {Class<? extends Annotation> annotationClass = e.getKey();// 如果isInherited,则添加到目标annotations当中if (AnnotationType.getInstance(annotationClass).isInherited()) {if (annotations == null) { // lazy constructionannotations = new LinkedHashMap<>((Math.max(declaredAnnotations.size(),Math.min(12, declaredAnnotations.size() + superAnnotations.size())) * 4 + 2) / 3);}annotations.put(annotationClass, e.getValue());}}
}
注解的基础方法

要想操作注解,首先必须获得类类型(也就是Class),常用的注解的基础方法如下:
首先getDeclaredAnnotationsgetAnnotation前面已经使用过。

Class<SessionScopedUserService> aClass = SessionScopedUserService.class;
// 获取该类上的注解
Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();
// 查找特定的注解
Qualifier qualifier = aClass.getAnnotation(Qualifier.class);
Component component = aClass.getAnnotation(Component.class);

通过getAnnotation方法获取到对应注解类型的实例之后,就可以获取对应的属性值了。比如如下注解定义了三个属性值(valuescopeNameproxyMode)。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {@AliasFor("scopeName")String value() default "";@AliasFor("value")String scopeName() default "";ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;}

如果要获取对应的属性值,如下所示

Scope scope = aClass.getAnnotation(Scope.class);
String value = scope.value();
String scopeName = scope.scopeName();
ScopedProxyMode scopedProxyMode = scope.proxyMode();

对于以上注解,scopeName既可以认为是属性,同样也可以认为是方法。所以也可以通过如下方式去获取这些值。

// annotationType.getDeclaredMethods()  获取注解上面的方法 一般也被称为属性
Method[] declaredMethods = annotationType.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {// 获取属性方法的参数个数int parameterCount = declaredMethod.getParameterCount();// 获取属性名称String name = declaredMethod.getName();// 获取属性默认值Object defaultValue = declaredMethod.getDefaultValue();// 通过反射获取属性值(此处属性参数个数为0个 因此args为null)Object invoke = declaredMethod.invoke(declaredAnnotation, null);
}

使用如下所示

package com.example.methodinjection;import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;//@SessionScope
//@RestController("sessionScopedUserService")
@Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedUserService extends AbstractUserService {// ...
}

通过对象直接获取属性值
在这里插入图片描述
通过方法获取属性值,首先获取到所有的方法
在这里插入图片描述
遍历方法依次获取属性值
在这里插入图片描述

元注解与复合注解

所谓的元注解就是作为注解的注解。上面介绍过注解的@Target,如果这个属性当中包含有ElementType.ANNOTATION_TYPE,就说明可以作用于注解上面。其实Target本身就是这样一个注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {/*** Returns an array of the kinds of elements an annotation type* can be applied to.* @return an array of the kinds of elements an annotation type* can be applied to*/ElementType[] value();
}

使用元注解的第一个好处是对一些注解进行分类了。比如在Spring当中

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {/*** The value may indicate a suggestion for a logical component name,* to be turned into a Spring bean in case of an autodetected component.* @return the suggested component name, if any (or empty String otherwise)*/@AliasFor(annotation = Component.class)String value() default "";}

这三个注解都包含了元注解@Component,因此都能作为引入Bean的注解。

By default, classes annotated with @Component, @Repository, @Service, @Controller, or a custom annotation that itself is annotated with @Component are the only detected candidate components.

但是却赋予了不同的语义。@Repository使用在数据层,而@Service使用在服务层,@Controller使用在控制层。因为这三个注解可以认为是继承了@Component,包含了父亲的逻辑,同时又可以单独扩展自己的逻辑。

Spring provides further stereotype annotations: @Component, @Service, and @Controller.@Component is a generic stereotype for any Spring-managed component. @Repository, @Service,and @Controller are specializations of @Component for more specific use cases, for example,in the persistence, service, and presentation layers, respectively. Therefore, you can annotate your component classes with @Component, but by annotating them with @Repository, @Service, or @Controller instead, your classes are more properly suited for processing by tools or associating
with aspects. For example, these stereotype annotations make ideal targets for pointcuts. It is also possible that @Repository, @Service, and @Controller may carry additional semantics in future releases of the Spring Framework. Thus, if you are choosing between using @Component or @Service for your service layer, @Service is clearly the better choice. Similarly, as stated above, @Repository is already supported as a marker for automatic exception translation in your persistence layer.

Repeatable annotation 可重复注解

如果我们在同一个类上面使用同一个注解,如下图所示:
在这里插入图片描述
报错内容如下所示

Duplicate annotation. The declaration of 'org.springframework.context.annotation.ImportResource' does not have a valid java.lang.annotation.Repeatable annotation

意思很明显,重复注解,@ImportResource不包含@Repeatable元注解。
这是怎么回事呢?

不妨看另一个注解,
在这里插入图片描述
可以看到此时是可以的。
更奇怪的是下面这个结果
在这里插入图片描述
通过getDeclaredAnnotations方法获取到的注解类型为org.springframework.context.annotation.ComponentScans.这个和org.springframework.context.annotation.ComponentScan有什么关系么?

源码如下所示

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {ComponentScan[] value();}

ComponentScans注解中包含了ComponentScan注解数组属性。
再看看ComponentScan源码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {/*** Alias for {@link #basePackages}.* <p>Allows for more concise annotation declarations if no other attributes* are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.*/@AliasFor("basePackages")String[] value() default {};.... 省略部分}

包含元注解信息@Repeatable(ComponentScans.class)。初次看起来会觉得很奇怪,在ComponentScan的元注解当中包含ComponentScans,而在ComponentScans注解当中包含了ComponentScan数组。这样的注解使用还蛮多的。比如在mybatis-spring当中org.mybatis.spring.annotation.MapperScanorg.mybatis.spring.annotation.MapperScans也是类似的关系。通过解析注解进行注册相关bean

 /*** A {@link MapperScannerRegistrar} for {@link MapperScans}.* * @since 2.0.0*/static class RepeatingRegistrar extends MapperScannerRegistrar {/*** {@inheritDoc}*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AnnotationAttributes mapperScansAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));if (mapperScansAttrs != null) {AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");for (int i = 0; i < annotations.length; i++) {registerBeanDefinitions(annotations[i], registry, generateBaseBeanName(importingClassMetadata, i));}}}}
Spring中解析Annotation的工具类

jdk提供的解析注解的方法只能获取一个类上的注解以及父类上面的可继承注解。而对于注解上的元注解中包含的元注解以及接口上面的注解、继承结构上(父亲、爷爷…)类的注解不可以直接获取。Spring提供了org.springframework.core.annotation.AnnotationUtils工具类,可以方便处理以上的情况。
在这里插入图片描述
首先是针对这个类的说明,用于注解的通用工具方法,可用于处理元注解、桥接方法(编译器为泛型方法生成的替代方法)、父类方法(针对可继承的注解@Inherited)
一般使用AnnotationUtils#findAnnotation(java.lang.reflect.Method, java.lang.Class<A>)AnnotationUtils#getAnnotation(java.lang.reflect.Method, java.lang.Class<A>)来代替JDK提供的方法。这个是针对于获取方法上面的注解。其实AnnotationUtils#getAnnotation(java.lang.reflect.AnnotatedElement, java.lang.Class<A>)AnnotationUtils#getAnnotation(java.lang.reflect.AnnotatedElement, java.lang.Class<A>)更方便使用。其中java.lang.reflect.AnnotatedElement既可以指代类,也可以指代方法以及其他包含注解的元素。
比如

Class<SessionScopedUserService> aClass = SessionScopedUserService.class;
// 获取类上面的注解
Component getComponent = AnnotationUtils.getAnnotation(aClass, Component.class);
Component findComponent = AnnotationUtils.findAnnotation(aClass, Component.class);
// 获取方法上的注解
for (Method method : aClass.getMethods()) {AnnotationUtils.getAnnotation(method, Component.class);
}

其中getAnnotationfindAnnotation的区别在于findAnnotation可以在整个继承结构上查找特定的注解。
比如在上面的例子当中SessionScopedUserService不包含@Component注解,而在父类中包含。通过getAnnotation方法是无法获取的,而通过findAnnotation是可以获取的。
在这里插入图片描述

getAnnotation源码如下

public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {try {A annotation = annotatedElement.getAnnotation(annotationType);if (annotation == null) {// 遍历元注解查找所有的注解for (Annotation metaAnn : annotatedElement.getAnnotations()) {annotation = metaAnn.annotationType().getAnnotation(annotationType);if (annotation != null) {break;}}}return (annotation != null ? synthesizeAnnotation(annotation, annotatedElement) : null);}catch (Throwable ex) {handleIntrospectionFailure(annotatedElement, ex);return null;}
}

从源码可以看出,getAnnotation会遍历所有的元注解依次查找指定注解。不过这已经比jdk提供的方法要强一些了。在SessionScopedUserService类上面添加注解@Service("service").
在这里插入图片描述
此时通过getAnnotation可以查找到@Component注解(@Component@Service的元注解)
在这里插入图片描述
findAnnotation源码如下:

public static <A extends Annotation> A findAnnotation(Class<?> clazz, @Nullable Class<A> annotationType) {return findAnnotation(clazz, annotationType, true);
}

缓存结果,如果已经解析过,再次查找,可以直接享受缓存带来的好处

private static <A extends Annotation> A findAnnotation(Class<?> clazz, @Nullable Class<A> annotationType, boolean synthesize) {Assert.notNull(clazz, "Class must not be null");if (annotationType == null) {return null;}AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);A result = (A) findAnnotationCache.get(cacheKey);if (result == null) {result = findAnnotation(clazz, annotationType, new HashSet<>());if (result != null && synthesize) {result = synthesizeAnnotation(result, clazz);findAnnotationCache.put(cacheKey, result);}}return result;
}

遍历元注解、接口、父类查找特定的注解。

private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {try {A annotation = clazz.getDeclaredAnnotation(annotationType);if (annotation != null) {return annotation;}// 遍历元注解for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) {Class<? extends Annotation> declaredType = declaredAnn.annotationType();if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {annotation = findAnnotation(declaredType, annotationType, visited);if (annotation != null) {return annotation;}}}}catch (Throwable ex) {handleIntrospectionFailure(clazz, ex);return null;}// 遍历接口	for (Class<?> ifc : clazz.getInterfaces()) {A annotation = findAnnotation(ifc, annotationType, visited);if (annotation != null) {return annotation;}}// 遍历父类	Class<?> superclass = clazz.getSuperclass();if (superclass == null || superclass == Object.class) {return null;}return findAnnotation(superclass, annotationType, visited);
}

从上面的源码不难看出,findAnnotation要比getAnnotation要复杂得多,实际使用的时候按需使用。如果简单的使用一次而且不需要遍历父类或结果、元注解的话,直接使用getAnnotation就可以了。


http://chatgpt.dhexx.cn/article/rIs7oacM.shtml

相关文章

Matlab中annotation函数的使用

目录 语法 说明 示例 创建文本箭头注释 创建文本框注释 创建包含多行文本的文本框注释 创建矩形注释 创建椭圆注释 组合使用两种类型的注释 创建后修改注释 annotation函数是给绘制的图形创建注释。 语法 annotation(lineType,x,y)annotation(lineType)annotation(…

java中Annotation详解

Java 注解&#xff08;Annotation&#xff09;又称 Java 标注&#xff0c;是 JDK5.0 引入的一种注释机制。主要作用&#xff1a;Annotation其实是代码里的特殊标记&#xff0c;这些标记可以在编译、类加载、运行时被读取&#xff0c;并执行相应的处理。通过使用Annotation&…

深入理解Java注解类型(@Annotation)

【版权申明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; http://blog.csdn.net/javazejian/article/details/71860633 出自【zejian的博客】 关联文章&#xff1a; 深入理解Java类型信息(Class对象)与反射…

【Spring AOP】@Aspect结合案例详解(一): @Pointcut使用@annotation + 五种通知Advice注解(已附源码)

文章目录 前言AOP与Spring AOPAspect简单案例快速入门 一、Pointcutannotation 二、五种通知Advice1. Before前置通知2. After后置通知3. AfterRunning返回通知4. AfterThrowing异常通知5. Around环绕通知 总结 前言 在微服务流行的当下&#xff0c;在使用SpringCloud/Springb…

Annotation理解及运用

什么是Annotation 我们在平时的开发过程中看到很多如@Override,@SuppressWarnings,@Test等样式的代码就是注解,注解是放到类、构造器、方法、属性、参数前的标记。 Annotation概念 Annontation是Java5开始引入的新特征。中文名称一般叫注解。它提供了一种安全的类似注释的…

Annotation介绍

Annotation思维导图

AnnotationProcessor 处理器不工作怎么定位?

什么是 Annotation Processor 构建问题 写过自定义注解处理器的老司机们乍一看这个问题觉得挺简单&#xff0c;是的&#xff0c;因为网上基本通篇都在教你怎么打日志&#xff0c;但是你有没有想过如果连日志都打印不出来的时候你怎么定位呢&#xff1f;譬如如下代码&#xff1…

annotation-driven 配置详解

一、前沿 在 Spring MVC 的项目中&#xff0c;我们经常使用 <mvc:annotation-driven> 这个配置&#xff0c;那么这个配置到底是做什么的呢&#xff1f;下面来分析一下&#xff0c;首先找到 mvc 的命名空间的定义&#xff0c;如下图&#xff1a; 从上述图中可知&#xff…

深入JAVA注解(Annotation):自定义注解

一、基础知识&#xff1a;元注解 要深入学习注解&#xff0c;我们就必须能定义自己的注解&#xff0c;并使用注解&#xff0c;在定义自己的注解之前&#xff0c;我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。 元注解&#xff1a; 元注解的作用就是负责注解其…

Java:annotation注解的简单理解和总结

Java annotation 注解Annotation1、Annotation的概述1.1、定义1.2、Annotation作用分类1.3、Annotation 架构 2、Annotation的语法形式3、Annotation的分类3.1、基本注解3.2、元注解3.2.1、Target3.2.2、Retention3.2.3、Documented3.2.4、Inherited: 4、自定义annotation4 .1、…

java注解的jar包_Java 注解 Annotation

目录: 从 JDK5 开始,Java 增加了对元数据(MetaData)的支持,也就是 Annotation。Annotation 其实就是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。…

Java Annotation Processing 概述

文章目录 JAVA ANNOTATION PROCESSING引言代码实现annotation-processor实现一个 Processor创建 AbstractProcessor 的子类 app 测试Reference JAVA ANNOTATION PROCESSING 引言 源码级别的注解处理最开始出现于 JAVA 5 中, 它提供了一种在编译期生成额外 “源文件” 的机制.…

annotation matlab,matlab 关于annotation函数的一点用法

这几天做毕设&#xff0c;用到了matlab的一些功能&#xff0c;先吐槽一下matlab的官方api写的是真的不好&#xff0c;很多东西不解释清楚。 首先对于所有的annotation函数&#xff0c;里面不论是维度还是大小参数&#xff0c;都是归一化到[0,1]之间的&#xff0c;也就是说&…

Android AnnotationProcessor

Android AnnotationProcessor 一.项目结构二.定义注解三.实现注解处理器(一)依赖(二)注解处理器(三)处理注解 四.使用注解处理器(一)依赖(二)使用注解(三)生成的代码 五.注意事项 注解处理器通常可以用在模块间解藕、自动生成代码等地方&#xff0c;比如router路由或者butterkn…

Annotation Processor

annotationProcessor和android-apt的功能是一样的&#xff0c;它们是替代关系。annotationProcessor是APT工具中的一种&#xff0c;他是google开发的内置框架&#xff0c;不需要引入&#xff0c;可以直接在build.gradle文件中使用。android-apt是由一位开发者自己开发的apt框架…

annotation是什么,用处,举例

1.概念&#xff1a;注解Annotation是java 1.5的新特性&#xff0c;是一种能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解&#xff0c;可用来将信息元数据与程序元素进行关联。Annotation 中文常译为“注解”。 2.用处&#xff1a; (1)生成文档。这…

Web大学生网页作业成品:个人博客主页 (纯HTML+CSS代码)

Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 个人博客网站 | 个人主页介绍 | 个人简介 | 个人博客设计制作 | 等网站的设计与制作 | 大学生个人HTML网页设计作品 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&…

vue搭建博客

使用vue2.0/3.0搭建博客&#xff0c;前端情绪员 实现原理安装nodejs和vue写项目项目打包和托管项目创建github仓库安装git托管项目到github仓库检查仓库并设置pages购买域名和解析域名修改更新项目ps(gitee搭建vue博客) 实现原理 使用vue打包出来的dist&#xff0c;把dist中的…

博客中动态图的制作

在博客中的动态图片的制作&#xff0c;可以在Android Studio里面连上手机进行视频的录制。之后用在视频的基础上用gifcam软件转化成gif动态图&#xff1a; 下载gitcam绿色版软件进行视频的处理&#xff1a; 打开GIFcam转化成gif格式的动态图&#xff1a;

手把手,从零开始搭建个人博客网站(附源码)

项目介绍 一、项目功能&#xff1a; 实现登录功能 密码数据加密 用户管理功能&#xff08;用户增删改查&#xff09; 文章管理功能&#xff08;文章增删改查&#xff09; 文章首页展示 文章评论功能&#xff08;需登录&#xff09; 首页文章展示、 用户管理、文章管理的…