java基础-注解Annotation原理和用法

在很多java代码中都可以看到诸如@Override、@Deprecated、@SuppressWarnings这样的字符,这些就是注解Annotation。注解最早在jdk5中被引入,现在已经成为java平台很重要的一部分了,很多的框架程序中也喜欢使用注解,如Spring、Mybatis等。

那么,什么是注解呢?注解就是元数据,一种描述数据的数据,通俗一点就是为程序的元素(类、方法、成员变量)加上更直观的说明,这些说明信息是与程序的业务逻辑无关的。但是,我们可以通过java的反射机制来获取Annotation的信息,并根据这些信息来对程序进行赋值、分发等操作。

java5.0定义了4个标准的meta-annotation元注解,它们被用来提供对其它annotation类型作说明,四种元注解如下:

  • @Target;
  • @Retention;
  • @Inherited;
  • @Documented;

下面详细说明这四种元注解的作用:

@Target

被用于描述注解的使用范围,即注解可以用在所修饰对象的什么地方,取值可以是ElementType中的一种:

  • CONSTRUCTOR:用于描述构造器;
  • FIELD:用于描述域;
  • LOCAL_VARIABLE:用于描述局部变量;
  • METHOD:用于描述方法;
  • PACKAGE:用于描述包;
  • PARAMETER:用于描述参数;
  • TYPE:用于描述类、接口、注解类型或枚举;
@Target(ElementType.TYPE)
public @interface Exculde{
    /**
    * 名称,默认值为""
    * @return
    */
    public String name() default "";
}

@Target(ElementType.FIELD)
public @interface Inject{
    /**
    * id,默认值为""
    * @return
    */
    public String id() default "";

    /**
    * 类,默认值为""
    * @return
    */
    public Class clazz() default Object.class;

}

上面定义了两个注解,注解@Exculde只能用于修饰类、接口、注解、枚举,注解@Inject只能修饰类型的域,如:

@Exculde(name="admin")
public class User{
     @Inject(id="username",clazz=String.class)
     private String username;
}

@Retention

用于描述注解的生命周期,即注解能在源码到JVM装载过程中的哪一个级别上有效。有些annotation仅出现在源码中,被编译器丢弃;有些annotation能被编译进class文件中,可能被JVM忽略;有些annotation不但能够被编译进class文件,而且能够在class文件被装载时被读取。这三种情况对应RetentionPoicy的三种取值:

  • SOURCE:源码文件中保留;
  • CLASS:class文件中保留;
  • RUNTIME:运行时保留;

@Inherited

用于描述注解是可以被继承的,如果一个使用了@Inherited修饰的annotation被用于一个class,那么这个annotation也将被用于这个class的子类。@Inherited是一个标记注解,没有参数选项,它修饰的annotation是被标记的class的子类所继承,类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当使用java的反射去获取一个@Inherited修饰的annotation时,反射检查将递归检查,检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

@Documented

用于描述注解信息应该被作为被标注的程序的公共API,即应该把注解信息保留文档中。@Documented也是一个标记注解,没有参数选项。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Exculde{
    /**
    * 名称注解,默认值为""
    * @return
    */
    public String name() default "";
}

如果你看过了上面的元注解,如果还不能理解也没关系,下面我们通过自定义注解来进一步理解元注解。

自定义注解需要使用@interface,类似于定义一个类使用class,但定义注解时不能再继承其它的类或者接口,它已经自动继承了java.lang.annotation.Annotation接口。@interface用来声明一个注解,其中的每一个方法实际上声明了一个配置参数,方法的名称就是参数的名称,方法的返回值类型就是参数的类型,也可以使用default来声明参数的默认值。

定义注解的格式如下:

public @interface 注解名{ 定义体 }

注解参数可支持的数据类型如下:

  • 基本类型;
  • Class;
  • String;
  • Enum;
  • Annotation;
  • 以上所有类型的数组形式;

下面定义一个类似于Spring的注解,用于向实例对象注入属性的值:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject{
    /**
    * ID,默认值为""
    * @return
    */
    public String id() default "";

    /**
    * 类,默认值为""
    * @return
    */
    public Class clazz() default Object.class;

}

把@Inject标注在类UserAction的属性上:

public class UserAction {
     @Inject(id="userService",clazz=UserService.class)
     private UserService userService;
}

在IOC容器框架中,对象都会被自动初始化,如果我们要实现IOC的这种功能,我们应该为加上@Inject注解的属性userService注入它的值。首先我们应该通过反射获取userService的域对象field,通过field获取@Inject注解的信息,然后根据注解的id和clazz得到它依赖的值:

Inject inject = field.getAnnotation(Inject.class);
String id = inject.id();
Class clazz = inject.clazz();

Object userService = Class.forName(clazz.getName).newInstance();
field.setAccessible(true);
field.set(object,userService);

上面代码中,调用field.getAnnotation(Inject.class)获取到@Inject的对象,然后获取@Inject的id和clazz值,通过反射实例化clazz的对象,再反射赋值给field。这就是Spring那些框架的依赖注入的实现原理,有兴趣的可以自己再优化一下。

读取类的注解信息还有其它的几个方法,在此不再一一说明,可以自行研究java.lang.reflect包。经过上面的说明,由此我们也可以知道注解仅仅是一种元数据,增强类、属性、参数的描述,使用注解的关键在于获取注解的信息,再通过反射的手段来实现注解想达成的功能。

文章同步发布在朗度云网站,传送门:

http://www.wolfbe.com/detail/201608/265.html

时间: 2024-10-14 10:19:11

java基础-注解Annotation原理和用法的相关文章

Java基础笔记 – Annotation注解的介绍和使用 自定义注解

Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 |  被围观 25,969 views+ 1.Annotation的工作原理:2.@Override注解:3.@Deprecated注解:4.@SuppressWarnings注解:5.自定义注解:5.1.添加变量:5.2.添加默认值:5.3.多变量使用枚举:5.4.数组变量:6.设置注解的作用范围:6.1.在自定义注解中的使用例子:7.使用反射读取R

Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义.解析,并对几个 Android 开源库 Annotation 原理进行简析.PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 完整版原文见:Java Android 注解(Ann

Java:注解Annotation(元数据)

本文内容: 注解Annotation的介绍 基本注解的用法 首发日期:2018-07-28 注解Annotation的介绍 Annotation是代码中的特殊标记,能够在编译.类加载.运行时被识别(需要设置),并根据不同的Annotation来执行不同的处理. Annotation可以修饰包.类.构造器.函数.成员变量.局部变量的声明.参数等程序元素.Annotation帮助这些元素来设置元数据,程序从元数据中获取信息来处理这些元素. 元数据可以描述代码间关系或者代码与其它资源的关系,比如说在w

Java自定义注解Annotation的使用

从 jdk5开始,Java增加了对元数据的支持,也就是Annotation,Annotation其实就是对代码的一种特殊标记,这些标记可以在编译,类加载和运行时被读取,并执行相应的处理.当然刚刚说了,Annotation只是一种标记,所以要是在代码里面不用这些标记也是能完成相应的工作的,只是有时候用注解能简化很多代码,看起来非常的简洁. 常见的注解(Annotation) @Override——限定重写父类方法 @Deprecated——标示已过时 @SuppressWarning——抑制编译器

Rhythmk 一步一步学 JAVA (19): 注解 annotation

在编写注解的时候需要了解的四种注解: @Target 表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类.接口(包括注解类型)或enum声 @Retention 表示需要在什么级别保存该注解信息.可选的RetentionPolicy参数包括: SOURCE:注解将被编译器

Java基础——注解

一.概述 引自百度百科: 定义:注解(Annotation),也叫元数据.一种代码级别的说明.它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明,注释. 二.JDK内置注解 Java提供了三种内建注解. 1. @Override——当我们想要复写父类中的方法时,我们需要使用该注解去告知编译器我们想要复写这个方法.这样一来当父类中的方法移除或者发生更改时编译器将提示错误信息. 2. @Depr

Java自定义注解Annotation详解

注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事.标记可以加在包,类,字段,方法,方法的参数以及局部变量上. 自定义注解及其应用 1).定义一个最简单的注解 public @interface MyAnnotation { //...... } 2).把注解加在某个类上: @MyAnnotation public class Annot

java基础:运算符的基本用法

运算符: 就是对常量和变量进行操作的符号. 算数运算符: A:+,-,*,/,%,++,-- B:+的用法 a:加法 b:正号 c:字符串连接符 C:/和%的区别 数据做除法操作的时候,/取得是商,%取得是余数 D:++和--的用法 a:他们的作用是自增或者自减 b:使用 **单独使用 放在操作数据的前面和后面效果一样. a++或者++a效果一样. **参与操作使用 放在操作数的前面:先自增或者自减,再参与操作 int a = 10; int b = ++a; 放在操作数的后面:先参与操作,再自

Java 基础【01】 This 用法

this表示类实例本身. this的用法 1.表示对当前对象的引用! 1 2 3 4 5 public class A{    public A getA(){       return this;//表示获取当前实例本身    } } 2.表示类的成员变量,而非函数参数,注意在函数参数和成员变量同名是进行区分! 1 2 3 4 5 6 public class A{    private int a = 0;//位置1    public A getA(int a){       this.a