Java Annotation基础详解

一 Java Annotation前世今生(这部分网上摘抄的,主要帮助理解)


1、Java Annotation因何而来?


    最初从印象中,是可以替代之前JDK1.4开发中,大量繁琐的配置项,Annotation的出现其实可以极大简化配置文件的数量和需要关注配置的内容。在阅读了诸多文章之后,发掘,这个取代配置项只是一个附带的结果,或者是我们开发者从中受益最多的方面罢了。其实,它背后的原因远不是如此。

    我们还是引用Wiki百科的观点来综述原因吧,a form of syntactic metadaa that can be added to Java source code,就是说,Annotation的引入是为了从Java语言层面上,为Java源代码提供元数据的支持!

    那第二个问题就来了,何为metadata(元数据)呢 ?

    Wiki官方的解释是Metadata is "data about data" 精辟呀,用来描述数据本身的数据。如果我们把Java的源代码看做是数据的话,那么metadata就是为了描述这些源代码而产生和使用的元数据,Annotation就是在Java语言层面上实现了metadata机制。

    Metadata分为两个方面的内容:   structural metadata和descriptive metadata,简而言之,就是结构性的元数据和描述性的元数据两种,当然这里的两种划分是否准确有待商榷,但是却让大致明白了它所应用的场景和场合。

    那是否有童鞋依然对Metadata一头雾水呢? 下面我们来举几个小例子,帮助大家感知一下元数据吧。比如我们从网上搜索一部电影的信息,电影本身是一个数据,可以播放和观看娱乐消遣。但我们如何才能找到我们想要的电影呢?想一想,对了,我们可以按照主演、导演、上映时间、影片类型、观众评分、票房收入和发行公司等诸多的信息进行搜索。这些我们搜索的条件,就是我们这里所谓的关于电影本身的metadata,他们都是用来描述电影本身的数据,但是不影响电影本身的播放和观看的。

2、Java Annotation的历史简述

    在引入Annotation之前,Java中其实已经有了类似的东西,比如transient和@deprecated。在2004年正式被JCP接受,在JDK5中正式引入的,主要是通过开发包中的apt命令来进行处理。 在JDK6中,将其集成到javac, 允许用户自定义Annotation,为用户自行扩展开启了通道。

二、概念、原理和作用

1、概念

Annontation(注解)是Java5开始引入的新特征。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。更通俗的意思是为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且是供指定的工具或框架使用的。

    Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

2、原理

    Annotation其实是一种接口。通过Java的反射机制相关的API来访问annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。annotation是不会影响程序代码的执行,无论annotation怎么变化,代码都始终如一地执行。

    Java语言解释器在工作时会忽略这些annotation,因此在JVM 中这些annotation是“不起作用”的,只能通过配套的工具才能对这些annontaion类型的信息进行访问和处理。

Annotation与interface的异同:

1)Annotation类型使用关键字@interface而不是interface。

这个关键字声明隐含了一个信息:它是继承了java.lang.annotation.Annotation接口,并非声明了一个interface

2)Annotation类型、方法定义是独特的、受限制的。

Annotation 类型的方法必须声明为无参数、无异常抛出的。这些方法定义了annotation的成员:方法名成为了成员名,而方法返回值成为了成员的类型。而方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用 default和一个默认数值来声明成员的默认值,null不能作为成员默认值,这与我们在非annotation类型中定义方法有很大不同。

Annotation类型和它的方法不能使用annotation类型的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotation类型中使用generic,因为此方法能够用类转换将各种类型转换为Class。

3、作用

Annotation在Java可以像public, final等语法修饰一样使用, 用以修饰用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。另外对于允许自定义参数的Annotation还可以在声明中使用参数。

在Java中主要用在以下几个方面:

1)文档编制:

通过@Documented来标注是否需要在javadoc中出现。

2)编译器检查

通过Annotation的使用,可以调整和控制编译器的使用以及让编译器提供关于代码的更多的检查和验证,比如@Override,@SuppressWarning.

3)代码分析

这个是我们开发者从中受益良多的部分,通过Annotation的使用,可以让我们在代码运行中动态得去控制系统的行为,从而省去之前诸多的配置和冗余代码。

三 分类和内置注解介绍

1、分类

分类方法一:首先,根据Annotation本身的特征有没有输入参数,可以分为以下两个类型:

1)Marker Annotation:标识Annotation,该Annotation没有参数输入,更多类似于标识一个东西,类似于Java语言中的java.io.Serialable之类的接口,并无需要实现的方法。

示例:  @Override, 用以标识该方法需要覆盖父类的同名方法,Annotation无参数输入。

public class MarkerAnnotationTest {  
     @Override  // --- marker annotation  
     public String toString() {  
          //print out information  
     }  
}

2、普通Annotation

其可以基于Annotation进行参数的设置,例如@SuppressWarnings。示例如下:

// --- 省略其他方法  
@SuppressWarnings(value={"unchecked","fallthrough"})  
public void generalAnnotationMethod() {   
    //省略方法体   
}

在普通的Annotation中,通过选择设置参数值,可以提供更为强大的功能控制。这里的@SuppressWarnings只是提供了一个参数的使用,更多方法的使用可以参考其Java API的说明。

分类方法二: 第二种分类方法是根据Annotation的应用对象来划分,分为标注代码的Annotation和用来标注Annotation的Annotation,即Meta-Annotation,元标注。

 标注代码的Annotation:我们平时定义和使用的绝大多数都是用来标注代码的Annotation,直接应用在java中的包、类,方法和变量之中。

标注Annotation的Annotation(元标注):此类Annotation用以定义Annotation之时,需要进行声明和说明的内容,例如:

@Target(应用的对象)

@Rention(代码,class或运行环境)

@Documented(出现在javadoc中)

@Inherted(标注类型是否可以被继承)

2、内置注解介绍

1)基本内置类型

 在Java 1.5 之后,引入了三个基本的Annotation类型,分别为: @Override,@Deprecated, 

@SuppressWarnings.

(1) @Override:作用于方法级别,只存在与编译阶段使用,表示其重载了父类的方法;如果使用了@Override,实际上却并未覆盖父类方法,java编译器将提示一个编译错误。我们其实,可以将其理解为一个强化语法检查的断言,辅助开发者减少错误。

(2)@Deprecated:作用于方法各个级别的代码,在运行阶段使用。在使用@Deprecated修饰的方法、变量等之后,编译器将不鼓励使用这个被标注的程序元素。使用这种修饰可以扩散到子类中,例如在代码中通过继承或者覆盖的方式使用了这个@Deprecated标识的类型或者成员,即使继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要抛出警告信息。

 (3)@SuppressWarnings:在代码中,适用于所有代码级别,用以提示Java编译器Java编译器关闭对类、方法及成员变量的警告。对于代码中不希望反复看到的警告信息,可以通过这个注解来屏蔽,编译器将不再抛出警告。

2)Meta-Annotation(元标注)

主要功能是用以修饰Annotation,先看一个spring @Component中例子吧:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {

	String value() default "";

}

接下来,我们将分别对这些Meta-Annotation做详细讲解。

(1)@Target:限定Annotation所修饰的对象范围,范围包括packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标,一句话,这个Annotation可以用在什么地方。

可用的取值(ElementType)有:

CONSTRUCTOR:用于描述构造器

FIELD:      用于描述域

LOCAL_VARIABLE:   用于描述局部变量

METHOD:用于描述方法

PACKAGE:用于描述包

PARAMETER:用于描述参数

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


(2)@Rentention:

定义了该Annotation被保留的时间长短,或者说生命周期,即其会在哪个阶段被保留,并在哪个阶段会被使用到。其value的取值(RetentionPoicy)有:

SOURCE:在源文件中有效(即源文件保留)--出现在源代码中,而被编译器丢弃。

CLASS:在class文件中有效(即class保留)--被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略。

RUNTIME:在运行时有效(即运行时保留)--其被编译到class中,并在运行时,被读取和使用的。


(3)@Documented:描述其它类型的annotation被作为被标注的程序成员的公共API,从而被javadoc此类的工具文档化。标记注解,没有成员。

(4)@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类型被发现,或者到达类继承结构的顶层。就是说,查看查找@Inherited过的Annotation之时,需要反复的向上查找,方才可以。


四 annotation实现示例

说了那么多基础知识,估计都看蒙了吧,来个实例消化消化吧,啥也不多说,直接上代码:

(1)定义注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)   //知道什么意思吧:用于描述方法
@Retention(RetentionPolicy.RUNTIME)   //在运行时有效
@Documented  //描述
public @interface Name {   
    String originate();   
    String community();   
}

(2)使用注解

public class Bean_Test {
	private String name;
	private String value;

	//瞧下面就用的了刚刚定义的注解
	@Name(originate = "Name", community = "Name的get方法")
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	@Name(originate = "Value", community = "Value 的get方法")
	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

}

(3)实现注解

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

public class TestAnnotation {
	public static void main(String[] args) throws Exception {
		String CLASS_NAME = "Bean_Test";
		Class<?> clz = Class.forName(CLASS_NAME);

		// 把JavaEyer这一类有利用到@Name的全部方法保存到Set中去 
		Method[] method = clz.getMethods();
		Set<Method> set = new HashSet<Method>();
		for (int i = 0; i < method.length; i++) {
			boolean otherFlag = method[i].isAnnotationPresent(Name.class);
			if (otherFlag)
				set.add(method[i]);
		}
		for (Method m : set) {
			Name name = m.getAnnotation(Name.class);
			System.out.println(name.originate());
			System.out.println("创建的社区:" + name.community());
		}
	}
}

这篇文章主要是从网上摘抄,然自己对Annotation知其然,下章准备看看源码知其所以然。


转载于:https://my.oschina.net/shyann/blog/383470

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值