JAVA反射_注解

概念及作用

Java注解,从名字上看是注释,解释。但功能却不仅仅是注释那么简单。
注解(Annotation)即元数据,就是源代码的元数据
注解(Annotation)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻方便地使用这些数据(通过解析注解来使用这些数据).

注解(Annotation)是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。


1.作用

* 生成文档
* 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
* 在编译时进行格式检查,如@Override等
* 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。


包java.lang.annotation中包含所有定义自定义注解所需用到的原注解和接口。如接口java.lang.annotation.Annotation是所有注解继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。
该包同时定义了四个元注解,Documented,Inherited,Target(作用范围,方法,属性,构造方法等),Retention(生命范围,源代码,class,runtime)。
(元注解:自定义注解的时候用到的,也就是自定义注解的注解,也就是自定义注解的注释,解释)


@Target

@Target说明了所修饰注解的范围:该注解可被用于 packages、types(类、接口、枚举、Annotation类型)、
类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。
在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
 类型  用途
CONSTRUCTOR 用于描述构造器
FIELD 用于描述域
LOCAL_VARIABLE 用于描述局部变量
METHOD 用于描述方法
PACKAGE 用于描述包
PARAMETER 用于描述参数
TYPE 用于描述类、接口 或enum声明

@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;
而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,
而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。
使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:

类型 用途 说明
SOURCE 在源文件中有效 仅出现在源代码中,而被编译器丢弃
CLASS 在源文件中有效 被编译在class文件中
RUNTIME 在运行时有效 编译在class文件中

@Documented

作用:将注解包含在javadoc中,它代表着此注解会被javadoc工具提取成文档。


@Inherited

作用:允许子类继承父类中的注解

自定义注解

格式:

public @interface 注解名{
  定义体
}

注解参数的可支持数据类型:
所有基本数据类型(int,float,double,boolean,byte,char,long,short)
String 类型
Class类型
enum类型
Annotation类型
以上所有类型的数组


规则:

* 修饰符只能是public 或默认(default)
* 参数成员只能用基本类型byte,short,int,long,float,double,boolean八种基本类型和
String,Enum,Class,annotations及这些类型的数组
如果只有一个参数成员,最好将名称设为”value”
注解元素必须有确定的值,可以在注解中定义默认值,也可以使用注解时指定,非基本类型的值不可为null,常使用空字符串或0作默认值
在表现一个元素存在或缺失的状态时,定义一下特殊值来表示,如空字符串或负值


最简单的例子:

public @interface TestA {
//这里定义了一个空的注解,它能干什么呢。我也不知道,但它能用。
}

在下面这个程序使用它:

@TestA    //使用了类注解
public class UserAnnotation {
    
    @TestA //使用了类成员注解
    private Integer age;
    
    @TestA //使用了构造方法注解
    public UserAnnotation(){
        
    }
    @TestA //使用了类方法注解
    public void a(){
        @TestA //使用了局部变量注解
        Map m = new HashMap(0);
    }
    
    public void b(@TestA Integer a){ //使用了方法参数注解
        
    }
}


这个注解也太简单了吧,好像什么信息也不能传递。别急下面就来一步步完善它,也该四位元注解依次开始上场了。

@Target(ElementType.TYPE)
public @interface TestA {
 
}
测试类那边立马出现了一堆错误,除了类注解。我想到这,聪明的你立刻明白了这个元注解的意义了。是不是想当然的偷起懒来了?难道还有意外?细心的朋友应该发现了,我们的测试类少了一个属性没用,就是ElemenetType.PACKAGE。在我们的注解加上这个属性的元注解后,我们测试程序的元注解全部阵亡,不对,还有一个没加呢,好加上。package 包,想当然是加载 package 前面。即

@TestA package com.king.annotation;
 
什么,也报错?这就搞不明白了,不加在这加哪去呢。我也不知道了,不过这是编译错误,我们的IDE将错误给我们指出了,就是

  Package annotations must be in file package-info.java ,E文虽然不好,但这个简单的还是难不倒的,package注解必须定义在package-info.java中。package-info 又是什么东西,百度..。

OK,到此,@Target元注解就全部完成了。

第二个元注解: @Retention 参数 RetentionPolicy。有了前面的经验这个注解理解起来就简单多了,并且幸运的是这个注解还没有特殊的属性值。
简单演示下如何使用:

@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
 
}
第三和第四个元注解就不再举例了。

下面我们还是继续来深入的探讨下注解的使用。上面的例子都非常简单,注解连属性都没有。
OK,下面我们就来定义一个有属性的注解,并在例子程序中获取到注解中定义的值。


开始之前讲下定义属性的规则:

@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。
方法的名称就是参数的名称,返回值类型就是参数的类型(参考上面)。
可以通过default来声明参数的默认值。
代码:

/*
* 自定义注解 TestA
* 为方便测试:注解目标为类方法,属性及构造方法
* 注解中含有三个元素 id ,name和 gid;
* id元素 有默认值 0
*/
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<Long> gid();
}

下面改下我们的测试类:

@TestA(name="type",gid=Long.class) //类成员注解
public class UserAnnotation {
	@TestA(name="param",id=1,gid=Long.class) //类成员注解
	private Integer age;

	@TestA (name="construct",id=2,gid=Long.class)//构造方法注解
	public UserAnnotation(){
	}

	@TestA(name="public method",id=3,gid=Long.class) //类方法注解
	public void a(){
		Map<String,String> m = new HashMap<String,String>(0);
	}

	@TestA(name="protected method",id=4,gid=Long.class) //类方法注解
	protected void b(){
		Map<String,String> m = new HashMap<String,String>(0);
	}

	@TestA(name="private method",id=5,gid=Long.class) //类方法注解
	private void c(){
		Map<String,String> m = new HashMap<String,String>(0);
	}
	public void b(Integer a){
	}
}


下面到了最重要的一步了,就是如何读取我们在类中定义的注解。只要读取出来了使用的话就简单了。
JAVA既然增加了注解,肯定就增加了相关读取的API。
在java.lang.reflect包中新增了AnnotatedElement接口,JDK源码如下:
public interface AnnotatedElement {
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
Annotation[] getDeclaredAnnotations();
}

isAnnotationPresent:判断是否标注了指定注解
getAnnotation:获取指定注解,没有则返回null
getAnnotations:获取所有注解,包括继承自基类的,没有则返回长度为0的数组
getDeclaredAnnotations:获取自身显式标明的所有注解,没有则返回长度为0的数组

public class ParseAnnotation {
 
/**
* 简单打印出UserAnnotation 类中所使用到的类注解
* 该方法只打印了 Type 类型的注解
* @throws ClassNotFoundException
*/
public static void parseTypeAnnotation() throws ClassNotFoundException {
	Class clazz = Class.forName("com.tmser.annotation.UserAnnotation");
	Annotation[] annotations = clazz.getAnnotations();
	for (Annotation annotation : annotations) {
		TestA testA = (TestA)annotation;
		System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());
	}
}
/**
* 简单打印出UserAnnotation 类中所使用到的方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseMethodAnnotation(){
	Method[] methods = UserAnnotation.class.getDeclaredMethods();
	for (Method method : methods) {
		/*
		* 判断方法中是否有指定注解类型的注解
		*/
		boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
		if (hasAnnotation) {
			/*
			* 根据注解类型返回方法的指定类型注解
			*/
			TestA annotation = method.getAnnotation(TestA.class);
			System.out.println("method = " + method.getName()
			+ " ; id = " + annotation.id() + " ; description = "
			+ annotation.name() + "; gid= "+annotation.gid());
		}
	}
}
/**
* 简单打印出UserAnnotation 类中所使用到的方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseConstructAnnotation(){
	Constructor[] constructors = UserAnnotation.class.getConstructors();
	for (Constructor constructor : constructors) {
		/*
		* 判断构造方法中是否有指定注解类型的注解
		*/
		boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
		if (hasAnnotation) {
			/*
			* 根据注解类型返回方法的指定类型注解
			*/
			TestA annotation =(TestA) constructor.getAnnotation(TestA.class);
			System.out.println("constructor = " + constructor.getName()
			+ " ; id = " + annotation.id() + " ; description = "
			+ annotation.name() + "; gid= "+annotation.gid());
		}
	}
}

public static void main(String[] args) throws ClassNotFoundException {
	parseTypeAnnotation();
	parseMethodAnnotation();
	parseConstructAnnotation();
}
}




猜你喜欢

转载自blog.csdn.net/kingdam578/article/details/78552224