java 注解(Annotation)实现原理

一、注解的介绍

注解是java的一种语言特性,在java5的时候开始引入,位于java.lang.annotation包中。

概念:注解是用于给java代码附加元数据,可在编译时或运行时解析并处理这些元数据的方法。简单理解就是给特定的包名、类、方法、成员变量贴上一个特殊意义的标签,人看到这个标签就能清楚他的作用和作用范围,机器看到这个标签就会实现这个标签所对应的业务逻辑(如果有业务逻辑的情况下)。

二、注解的作用

  • 生成文档,通过代码里标识的元数据生成javadoc文档。

  • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。

  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。

  • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

重要的事情说三遍:

注解仅仅是元数据,和业务逻辑无关!!

注解仅仅是元数据,和业务逻辑无关!!

注解仅仅是元数据,和业务逻辑无关!!

三、注解的种类

  • 一类是Java自带的标准注解,包括@Override、@Deprecated和@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。

  • 一类为元注解,元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented,@Retention用于标明注解被保留的阶段,@Target用于标明注解使用的范围,@Inherited用于标明注解可继承,@Documented用于标明是否生成javadoc文档。

  • 一类为自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。 

四、元注解

  • @Target, – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。ElementType ( ElementType     ==> 作用域范围):

    1.CONSTRUCTOR:        用于描述构造器

    2.FIELD:                         用于描述域

    3.LOCAL_VARIABLE:    用于描述局部变量

    4.METHOD:                    用于描述方法

    5.PACKAGE:                  用于描述包

    6.PARAMETER:             用于描述参数

    7.TYPE:                       用于描述类、接口(包括注解类型) 或enum声明

  • @Retention,– 定义该注解的生命周期。

         SOURCE     对应源码阶段

         CLASS        对应编译阶段

         RUNTIME     随时,包含上面两个阶段

  • @Documented,–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。

  • @Inherited– 定义该注释和子类的关系

元注解

作用

取值

备注

@Target

指定了注解运用的地方

下面给出

你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。

@Retention

说明注解的存活时间

下面给出

我们可以这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。

@Document

说明注解是否能被文档化

不常用

@Inhrited

说明注解能否被继承

不常用

五、注解的实现方式

Annotations只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
   public enum Priority {LOW, MEDIUM, HIGH}
   public enum Status {STARTED, NOT_STARTED}
   String author() default "Yash";
   Priority priority()defaultPriority.LOW;
   Status status() default Status.NOT_STARTED;
}
@Todo(priority = Todo.Priority.MEDIUM, author ="Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
  //Some business logic is written
  //But it’s not complete yet
}

六、注解处理器

这个是注解使用的核心了,前面我们说了那么多注解相关的,那到底java是如何去处理这些注解的呢

从getAnnotation进去可以看到java.lang.class实现了AnnotatedElement方法

MyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement

java.lang.reflect.AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:

  方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
  方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
     方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
  方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响

七、注解的使用案例

各位看官可跳转到参考资料中最后两个链接,感谢这两篇文章的作者对源码和虚拟机底层实现进行剖析,讲解了Spring中@Autowire注解和java中@Override注解的实现原理。

从中我们能更加深入的体会到,注解仅仅是元数据,和业务逻辑无关!!

所有注解代表的业务逻辑,都是和注解本身剥离开的,在声明了注解的作用范围和存活时间后,通过反射机制,获取到注解对应的作用领域,进而对该包、类、方法、成员变量进行业务逻辑操作。

参考资料

Java注解的原理(通俗易懂)

Java 注解工作原理解析

JAVA 注解机制及其原理

Spring IOC原理源码解析(@Autowired原理详解 :标识属性与方法)(二 )

从虚拟机角度看Java多态->(重写override)的实现原理

发布了60 篇原创文章 · 获赞 57 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qixinbruce/article/details/89006163