1,什么是注解

注解也叫元数据,例如常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解

一般常用的注解可以分为三类:

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

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

  3. 一类为自定义注解,可以根据自己的需求定义注解

2,注解的用途

在看注解的用途之前,有必要简单的介绍下XML和注解区别,

注解:是一种分散式的元数据,与源代码紧绑定。

xml:是一种集中式的元数据,与源代码无绑定

当然网上存在各种XML与注解的辩论哪个更好,这里不作评论和介绍,主要介绍一下注解的主要用途:

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

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

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

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

3,注解使用演示

这边总共定义了4个注解来演示注解的使用

  1. 定义一个可以注解在Class,interface,enum上的注解,

  2. 定义一个可以注解在METHOD上的注解

  3. 定义一个可以注解在FIELD上的注解

  4. 定义一个可以注解在PARAMETER上的注解

具体代码如下:

/**
* 定义一个可以注解在Class,interface,enum上的注解
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetType {
   /**
    * 定义注解的一个元素 并给定默认值
    * @return
    */
   String value() default "我是定义在类接口枚举类上的注解元素value的默认值";
}
/**
* 定义一个可以注解在METHOD上的注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetMethod {
   /**
    * 定义注解的一个元素 并给定默认值
    * @return
    */
   String value() default "我是定义在方法上的注解元素value的默认值";
}
/**
* 定义一个可以注解在FIELD上的注解
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetField {
   /**
    * 定义注解的一个元素 并给定默认值
    * @return
    */
   String value() default "我是定义在字段上的注解元素value的默认值";
}
/**
* 定义一个可以注解在PARAMETER上的注解
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetParameter {
   /**
    * 定义注解的一个元素 并给定默认值
    * @return
    */
   String value() default "我是定义在参数上的注解元素value的默认值";
}

编写一个测试处理类处理以上注解,这边不过多解释,代码中都写了对应的注释如下:

/**
* 测试java注解类
*/
@MyAnTargetType
public class AnnotationTest {
   @MyAnTargetField
   private String field = "我是字段";
   @MyAnTargetMethod("测试方法")
   public void test(@MyAnTargetParameter String args) {
       System.out.println("参数值 === "+args);
   }
   public static void main(String[] args) {
       // 获取类上的注解MyAnTargetType
       MyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);
       System.out.println("类上的注解值 === "+t.value());
       MyAnTargetMethod tm = null;
       try {
           // 根据反射获取AnnotationTest类上的test方法
           Method method = AnnotationTest.class.getDeclaredMethod("test",String.class);
           // 获取方法上的注解MyAnTargetMethod
           tm = method.getAnnotation(MyAnTargetMethod.class);
           System.out.println("方法上的注解值 === "+tm.value());
           // 获取方法上的所有参数注解  循环所有注解找到MyAnTargetParameter注解
           Annotation[][] annotations = method.getParameterAnnotations();
           for(Annotation[] tt : annotations){
               for(Annotation t1:tt){
                   if(t1 instanceof MyAnTargetParameter){
                       System.out.println("参数上的注解值 === "+((MyAnTargetParameter) t1).value());
                   }
               }
           }
           method.invoke(new AnnotationTest(), "改变默认参数");
           // 获取AnnotationTest类上字段field的注解MyAnTargetField
           MyAnTargetField fieldAn = AnnotationTest.class.getDeclaredField("field").getAnnotation(MyAnTargetField.class);
           System.out.println("字段上的注解值 === "+fieldAn.value());
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

运行结果如下:

类上的注解值 === 我是定义在类接口枚举类上的注解元素value的默认值
参数上的注解值 === 我是定义在参数上的注解元素value的默认值
参数值 === 改变默认参数
方法上的注解值 === 测试方法
字段上的注解值 === 我是定义在字段上的注解元素value的默认值

4,注解的实现原理

以上只抽取了注解的其中几种类型演示,下面来看看他们是怎么工作的

先看一下实现注解三要素:

1,注解声明、

2,使用注解的元素、

3,操作注解使其起作用(注解处理器)

注解声明

首先让看一下java中的元注解(也就是上面提到的注解的注解),总共有4个如下:

  • @Target,

  • @Retention,

  • @Documented,

  • @Inherited

这4个元注解都是在jdk的java.lang.annotation包下面

@Target

Target说明的是Annotation所修饰的对象范围,源码如下:

@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();
}

其中只有一个元素ElementType,再看看它的源码如下:

public enum ElementType {
   /** Class, interface (including annotation type), or enum declaration */
   TYPE,
   /** Field declaration (includes enum constants) */
   FIELD,
   /** Method declaration */
   METHOD,
   /** Formal parameter declaration */
   PARAMETER,
   /** Constructor declaration */
   CONSTRUCTOR,
   /** Local variable declaration */
   LOCAL_VARIABLE,
   /** Annotation type declaration */
   ANNOTATION_TYPE,
   /** Package declaration */
   PACKAGE,
   /**
    * Type parameter declaration
    *
    * @since 1.8
    */
   TYPE_PARAMETER,
   /**
    * Use of a type
    *
    * @since 1.8
    */
   TYPE_USE
}

ElementType是一个枚举类定义注解可以作用的类型上,上面例子中演示了TYPE,FIELD,METHOD,PARAMETER 4种可以作用的目标

@Retention

定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个元注解可以对 Annotation的“生命周期”限制

@Documented

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员

@Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

写了一个例子,在以上MyAnTargetType注解类中增加@Inherited注解,如下:

/**
* 定义一个可以注解在Class,interface,enum上的注解
* 增加了@Inherited注解代表允许继承
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnTargetType {
   /**
    * 定义注解的一个元素 并给定默认值
    * @return
    */
   String value() default "我是定义在类接口枚举类上的注解元素value的默认值";
}

增加一个子类ChildAnnotationTest继承AnnotationTest测试如下:

/**
* 增加一个子类继承AnnotationTest 演示@Inherited注解允许继承
*/
public class ChildAnnotationTest extends AnnotationTest {
   public static void main(String[] args) {
       // 获取类上的注解MyAnTargetType
       MyAnTargetType t = ChildAnnotationTest.class.getAnnotation(MyAnTargetType.class);
       System.out.println("类上的注解值 === "+t.value());
   }
}

运行如下:

类上的注解值 === 我是定义在类接口枚举类上的注解元素value的默认值

说明已经获取到了父类AnnotationTest的注解了

如果MyAnTargetType去掉@Inherited注解运行则报错如下:

Exception in thread "main" java.lang.NullPointerException
   at com.zhang.run.ChildAnnotationTest.main(ChildAnnotationTest.java:17)

使用注解的元素

使用注解没什么好说的就是在你需要的地方加上对应的你写好的注解就行

注解处理器

这个是注解使用的核心了,前面我们说了那么多注解相关的,那到底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():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响

认识下java注解的实现原理的更多相关文章

  1. Java 注解及其底层原理

    目录 什么是注解? 注解的分类 Java自带的标准注解 元注解 @Retention @Documented @Target @Inherited @Repeatable 自定义注解 自定义注解的读取 ...

  2. Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. 使用Java注解来简化你的代码

         注解(Annotation)就是一种标签,可以插入到源代码中,我们的编译器可以对他们进行逻辑判断,或者我们可以自己写一个工具方法来读取我们源代码中的注解信息,从而实现某种操作.需要申明一点, ...

  4. java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题

    一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...

  5. Java注解的原理

    自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解.这篇文章中,我将向大家讲述 ...

  6. Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)

    Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ...

  7. Java 注解指导手册(下)

    9. 自定义注解   正如我们之前多次提及的,可以定义和实现自定义注解.本章我们即将探讨. 首先,定义一个注解:   public @interface CustomAnnotationClass   ...

  8. 浅谈Java/Android下的注解

    什么是注解 java.lang.annotation,接口 Annotation,在JDK5.0及以后版本引入. 注解是代码里的特殊标记,这些标记可以在编译.类加载.运行时被读取,并执行相应的处理.通 ...

  9. java@ 注解原理与使用

    Java反射 java反射机制的定义: 在运行转态时(动态的)时. 对于任意一个类,都能够知道这个类的所有属性和方法 对于任意一个对象,都能够知道调用它的任意属性和方法 Class对象 java中用对 ...

随机推荐

  1. C# 控件

    .ascx:Web窗体用户控件.用来存放独立的用户控件,可提供众多页面使用: <%@ Control Language="C#" AutoEventWireup=" ...

  2. Zookeeper Ha集群简介+jdbcClient访问Ha集群环境

    Hadoop-HA机制HA概述high available(高可用) 所谓HA(high available),即高可用(7*24小时不中断服务). 实现高可用最关键的策略是消除单点故障.HA严格来说 ...

  3. Linux VMware安装VMTools工具

    安装VMTools工具 2)先启动CentOS并成功登录如下图,发现底部提示且窗口中等大小,准备安装 3)选择虚拟机菜单栏--安装VMware tools 4)光驱自动挂载VMTools 5)右键解压 ...

  4. Pyhanlp自然语言处理中的新词识别

    新词发现 本“新词发现”模块基于信息熵和互信息两种算法,可以在无语料的情况下提取一段长文本中的词语,并支持过滤掉系统中已存在的“旧词”,得到新词列表. 调用方法 静态方法 一句话静态调用接口已经封装到 ...

  5. hadoop 2.7.1安装和配置

    一.安装环境 硬件:虚拟机 操作系统:Centos 6.4 64位 IP:192.168.241.128主机名:admin安装用户:root 二.安装JDK 安装JDK1.7或者以上版本.这里安装jd ...

  6. 解决 VUE 微信登录验证 【感谢原文:https://segmentfault.com/a/1190000009493199】

    [感谢原文:https://segmentfault.com/a/1190000009493199] SPA单页应用中微信授权登录的一点思路 单页应用应该如何解决微信授权登录的尴尬跳转?后退无法返回? ...

  7. vagrant 虚拟机中安装 lnamp 环境

    转载自 :http://git.oschina.net/apanly/mooc/tree/master/vagrant 替换源 sudo cp /etc/apt/sources.list /etc/a ...

  8. HTML5 上传前端html页面

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  9. 黄聪:jquery.bootgrid表格插件有的属性(visibleInSelection、cssClass、headerCssClass、headerAlign)不能识别的解决办法

    主要是属性大小写问题,修改jquery.bootgrid.js文件,在function loadColumns()方法里面添加下面的语句就好了 data.headerAlign = data.head ...

  10. 黄聪:wordpress如何获取访问的网站的cookie值

    $head = wp_get_http_headers( $word_url ); $cookie = $head['set-cookie']; $cookie = substr( $cookie, ...