一文学会Java注解Annotation

死牛胖子
• 阅读 406

注解

什么是注解?

注解(Annotation)是从JDK1.5开始引入的一种特性,与 Class、Interface、Enum 相同层次,可以像创建 Class 一样创建自己的 Annotation

注解(Annotation)是一种代码级别的元数据,可以标记在包、类型、成员变量、方法、参数的声明中

注解有什么用?

注解可以理解为一个标记,打在包、类型、成员变量、方法、参数的声明上的一个标记,在后续适合的时机对该标记进行解析,根据标记的内容执行相应的逻辑处理。

JDK 提供了 @Override、@Deprecated 等注解,这些注解都是用来规范代码,@Override 标记方法是一个重写方法,@Deprecated 标记方法/类是过时的,不推荐使用。

项目中经常会集成 Lombok 组件,该组件提供了 @Data、@Getter、@Setter 等一系列注解,在一个实体类上标记 @Data 注解,就可以不用为成员变量写 Getter&Setter 方法,但却可以使用 Getter&Setter 方法,就是因为在编译过程中,发现 @Data 注解,编译器自动为实体类的成员变量生成了 Getter&Setter 方法。

项目中经常会集成 HibernateValidator 组件用于参数校验,该组件提供了 @NotNull、@NotEmpty 等一系列注解,方法调用时检测到入参前有 @Valid 注解,组件就会对入参的成员变量进行值校验,如果不符合注解提供的规则,就会抛出异常。

元注解

什么是元注解?

元注解就是标记在注解上的注解。JDK 提供了以下几个元注解,在自定义注解时会用到。

  • @Target
  • @Retention
  • @Documented
  • @Inherited
  • @Repeatable

@Target

声明注解可以修饰的对象范围

  • TYPE:类、接口、枚举、注解
  • FIELD:成员变量
  • METHOD:方法
  • PARAMETER:方法参数
  • CONSTRUCTOR:构造方法
  • LOCAL_VARIABLE:局部变量
  • ANNOTATION_TYPE:注解
  • PACKAGE:包
  • TYPE_PARAMETER:类型参数(即泛型)
  • TYPE_USE:任何类型

如果声明为 ANNOTATION_TYPE 类型,说明当前注解是一个元注解,可以看一下 @Target 元注解本身的定义。

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

@Retention

可以理解为注解的生命周期,可以分为三个阶段,源代码(.java文件) -> 字节码(.class文件) -> 运行期,有的注解只作用于源代码,编译后就会被丢弃,有的注解可以正常编译到 class 文件,但程序运行时会被虚拟机忽略。

  • SOURCE:源代阶段,只出现在源代码中,在编译阶段会被编译器忽略
  • CLASS:编译阶段,会被编译进 .class 文件中,类加载过程中会被虚拟机忽略
  • RUNTIME:运行期阶段,会被虚拟机加载到 Class 对象中,可以通过反射得到这个注解。

@Documented

一个标记注解,没有成员,是否需要出现在 JavaDoc 文档中

@Inherited

注解是否可以被继承,在父类声明的注解,解析子类时,是否可以获取到该注解。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Anno1 {}

@Anno1
public class Base {}

public class Child extends Base {
    public static void main(String[] args) throws Exception {
        Annotation[] annotations = Child.class.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);
    }
}

如果父类与子类同时声明了同一个注解,相当于方法重写的意思,在解析子类时,只会解析到子类的注解,而不会解析到父类的注解

也不是所有标记了 @Inherited 的注解就可以被继承,只有标记在 class 上,才可以继承,也就是说标记在接口、成员变量、方法上,通过子类也无法解析到。

@Repeatable

正常一个元素上,相同的注解只能标记一个,如果注解上加了 @Repeatable 元注解,就可以标记多个

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(value = AnnoList.class)
public @interface Anno1 {
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoList {
    Anno1[] value();
}

@Anno1
@Anno1
public class Base {}

可以理解为实际标记的是一个 AnnoList 注解,在反射时,如果发现有多个,就会解析成 AnnoList,如果只有一个,就会解析成 AnnoList,这会直接影响到注解的继承(Inherited)。

自定义注解

注解在声明时,不支持继承其它的接口或者注解。 注解可以声明配置项,通过 default 设置默认值,格式如下。

如果只有一个配置属性,建议使用 value,注解在使用时如果没有设置配置项,默认使用的配置属性就是 value

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anno1 {
    String value() default "";
}

反射解析注解

对于 @Retention 为 RUNTIME 的注解,可以在运行期通过反射的方式得到注解,JDK 的反射模块为注解提供了两个重要的接口:Annotation 及 AnnotatedElement。

所有注解自动实现 Annotation 接口,由编译器自动完成继承细节。

注解标记的元素必须实现 AnnotatedElement 接口,如 Class、Field、Method 等类都实现了 AnnotatedElement 接口,AnnotatedElement 接口提供了获取注解的方法。

//方法一:返回元素上存在的所有注解
Annotation[] getAnnotations()
// 方法二:返回元素上指定类型的注解
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
// 方法三:针对 @Repeatable 注解,支持通过子注解获取
<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
// 方法四:判断元素上是否存在指定类型的注解
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    return this.getAnnotation(annotationClass) != null;
}

// 以上方法支持注解的继承
// 以下方法为 Declared 版本,只能解析当前元素的注解,不支持注解的继承。

// 方法五:返回元素上存在的所有注解
Annotation[] getDeclaredAnnotations()
// 方法六:返回元素上指定类型的注解
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
// 方法七:针对 @Repeatable 注解,支持通过子注解获取
<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java注解
来源:秒懂,Java注解(Annotation)你可以这样学(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.csdn.net%2Fqq1404510094%2Farticle%2Fdetails%2Fblog.csdn.net%2Fbriblue)Annotation
Stella981 Stella981
3年前
NacosValue 注解
NacosValue 定义在nacosapi工程中:com.alibaba.nacos.api.config.annotation.NacosValue注解解析在nacosspringproject工程中:com.alibaba.nacos.spring.util.NacosBeanUtilsregisterNacosValueAnn
Easter79 Easter79
3年前
SpringCloud Config手动刷新及自动刷新
1、Config手动刷新a、使用@RefreshScope注解importorg.springframework.beans.factory.annotation.Value;importorg.springframework.cloud.context.config.annotation.RefreshScope;
Wesley13 Wesley13
3年前
Java元注解
1、注解数据类型注解是写在.java文件中,使用@interface作为关键字,所以注解也是Java的一种数据类型,从广泛的定义来说,Class、Interface、Enum、Annotation都属于Class类型。2、元注解在创建注解的时候,需要使用一些注解来描述自己创建的注解(即注解的注解),就是写在@interfac
Easter79 Easter79
3年前
Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象
\注解扫面的bean的ID问题0.前提需要明白注解扫描出来的bean的id默认是类名首字母小写,当然可以指定id:
Stella981 Stella981
3年前
EntityFrameworkCore 中实体的几种配置方法
EntityFramework有几种方式可实现数据库表与实体的关系配置(relationship)1.convention2.annotation3.fluentapi使用数据注解实体类通常是在Models目录下,直接在实体类上添加属性注解,比如Required/Key等.
Stella981 Stella981
3年前
AOP实现Controller参数日志
packagecom.jie.common;importjava.lang.annotation.\;/\\\@authorwuchunjie\@date2018/2/23\/@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取@Target(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
3年前
Java Annotation详解 理解和使用Annotation
系统中用到了java注解:查了一下如何使用注解,到底注解是什么;(1)创建方法:MsgTraceJavaClass在CreateNewClass中:name:输入MsgTrace;Kind:Annotation;就可以了;public@interfaceMsgTrace{String
京东云开发者 京东云开发者
5个月前
警惕!自定义注解使用不当的排查实录
一、引言大家好,在日常开发过程中,Java注解(Annotation)是开发中经常使用的一个手段,用于给代码添加元数据的标记。它们可以提供代码额外的信息,这些信息可以在编译时或运行时被访问。注解不会改变代码的执行逻辑,但可以被编译器、JVM或框架等工具用于
死牛胖子
死牛胖子
Lv1
悲歌可以当泣,远望可以当归。
文章
7
粉丝
1
获赞
0