本篇文章将从元注解、自定义注解的格式、自定义注解与反射结合的简单范例、以及自定义注解的应用来说一说java中的自定义注解。

一、元注解

元注解也就是注解其他注解(自定义注解)的java原生的注解,Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited                                                                                                                                                                                   前两个比较常用,后两个基本很少用到。

 @Target:

   @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有以下7个枚举类型:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

  使用实例:  

@Target(ElementType.METHOD)
public @interface MyTarget
{
String value();
}

  @Retention:

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

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  取值(RetentionPoicy)有以下3个枚举类型:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

  Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}

  @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类型被发现,或者到达类继承结构的顶层。

二、自定义注解的格式

  public @interface 注解名 {定义体}

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

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

  当注解中属性名为value是,在对其赋值时可以不指定属性的名称而直接写上属性值即可,除了value以外的其他值都要使用name = value这种赋值方式,明确指定到底给谁赋值。

  一个方法可以由多个注解修饰

  当我们使用@interface自行定义Annotation时,实际上是自动继承了java.lang.annotation.Annotation接口,由编译程序自动为您完后其他产生的细节。在定义Annotation类的时候不能继承其他Annotation类型的接口。

三、自定义注解与反射结合使用的简单例子

 自定义注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name();
String pass();
}

  用@Target(ElementType.METHOD)定义MyAnnotation注解的使用时机是方法。

  用@Retention(RetentionPolicy.RUNTIME)定义MyAnnotation注解会被虚拟机读取。

 使用注解的类和方法:

public class MyTest {
@Deprecated
@MyAnnotation(name = "admin", pass = "*****")
public void output(){
System.out.println("output something!");
}
}

  用@Deprecated定义output方法是不建议被使用的方法。

  用@MyAnnotation(name = "admin", pass = "*****")注释output方法,并为属性赋值

 利用反射实现功能的类:

public class MyReflection {
public static void main(String[] args) throws Exception {
MyTest myTest = new MyTest();
Class<MyTest> c = MyTest.class;
Method method = c.getMethod("output", new Class[]{}); //判断该方法是否有MyAnnotation注解
if(method.isAnnotationPresent(MyAnnotation.class)) {
method.invoke(myTest, new Class[]{}); //通过反射获取myAnnotation对象
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
String name = myAnnotation.name();
String pass = myAnnotation.pass(); System.out.println(name + ", " + pass);
} //获取当前方法的所有注解的对象,注意必须是运行时注解这里才能获取。
//因为是通过反射获取的所以显然应该是运行时的注解才会获取。
Annotation[] annotations = method.getAnnotations(); //遍历输出注解的名字
for(Annotation annotation : annotations)
{
System.out.println(annotation.annotationType().getName());
}
}
}

 运行结果:

  output something!

  admin, *****

  java.lang.Deprecated

  wenge.annotation.MyAnnotation

这段程序的作用是用反射运行由自定义注解注释的方法,获取自定义注解的对象并进行操作(输出了属性),得到output方法的所有注解的对象。这里需要说明的一点是反射只能对运行时注解(也就是用@Retention(RetentionPolicy.RUNTIME)注释的注解)进行操作。

 这里就是用一个简单的例子说明一下自定义注解与反射的使用。

四、自定义注解与反射结合的应用场景

 我觉得自定义注解与反射最好的应用场景就是junit。在junit4以前是用方法名以test打头实现的,而在junit4以后就是应用自定义注解实现识别测试方法。但是不管怎么用说白了还是反射,只是用了一种比较好的实现方式而已。而且这种方式的确很优雅。

 junit执行的一般流程是:

  1、首先获得待测试类所对应的Class对象。

  2、然后通过改class对象获得当前类中所有public方法所对应的Method数组。

  3、遍历该method数组,取得每一个Method对象。

  4、调用每个Method对象的isAnnotationPresent(Test.class)方法,判断该方法是否被@test注释。

  5、如果isAnnotationPresent返回true,调用method.invoke()方法执行该方法,否则不执行。

 准备下次写一篇关于junit源码的文章,在这里就不细说了。

javase基础回顾(四) 自定义注解与反射的更多相关文章

  1. Java利用自定义注解、反射实现简单BaseDao

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先 ...

  2. java自定义注解与反射

    java注解与反射一.Java中提供了四种元注解,专门负责注解其他的注解,分别如下 1.@Retention元注解,表示需要在什么级别保存该注释信息(生命周期).可选的RetentionPoicy参数 ...

  3. java基础知识:自定义注解

    转自 深入了解注解 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解的作用就是负责注解其他注解.J ...

  4. JAVA-注解(2)-自定义注解及反射注解

    自定义注解开发 1.开发一个注解类 开发一个注解类的过程,非常类似于开发一个接口,只不过需要通过@interface关键字来声明 2.使用元注解修饰注解的声明 所谓的原注解是用来修饰注解声明的注释,可 ...

  5. springboot使用自定义注解和反射实现一个简单的支付

    优点: 未使用if else,就算以后增加支付类型,也不用改动之前代码 只需要新写一个支付类,给添加自定义注解@Pay 首先: 定义自定义注解 Pay 定义 CMBPay ICBCPay 两种支付 根 ...

  6. Spring AOP基础概念及自定义注解式AOP初体验

    对AOP的理解开始是抽象的,看到切点的匹配方式其实与正则表达式性质大致一样就基本了解AOP是基本是个什么作用了.只是整个概念更抽象,需要具化理解.下图列表是AOP相关概念解释,可能也比较抽象^_^ 比 ...

  7. Android中的自定义注解(反射实现-运行时注解)

    预备知识: Java注解基础 Java反射原理 Java动态代理 一.布局文件的注解 我们在Android开发的时候,总是会写到setContentView方法,为了避免每次都写重复的代码,我们需要使 ...

  8. JavaSE基础第四篇

    1.参数传递   2,方法的重载 方法的参数的个数.类型.顺序 跟修饰符.返回值无关   3.构造方法: return 表示当前方法执行结束,后面不能写任何语句   4工程导入 单个.java文件粘贴 ...

  9. javase基础回顾(一)ArrayList深入解析 解读ArrayList源代码(JDK1.8.0_92)

    我们在学习这一块内容时需要注意的一个问题是 集合中存放的依然是对象的引用而不是对象本身. List接口扩展了Collection并声明存储一系列元素的类集的特性.使用一个基于零的下标,元素可以通过它们 ...

随机推荐

  1. 使用SharedPreferences存储用户配置信息

    用SharedPreferences来保存用户的基本配置信息非常的方便,实现起来也很容易:以下是一个简单的例子: 效果截图:    主要代码: public class MainActivity ex ...

  2. JavaScript中的6种运算符总结

    JavaScript 运算符主要包括: 算术运算符 赋值运算符 比较运算符 三元运算符 逻辑运算符 字符串连接运算符 运算符 说明 例子 运算结果 + 加 y = 2+1 y = 3 - 减 y = ...

  3. JavaScript中数组类型的属性和方法

    除了Object,Array类型应该是ECMAScript中最常用的类型了. ECMAScript的数组虽然也是数据的有序列表,但还是与其他语言中的数组有很大的区别.比如ECMAScript数组每一项 ...

  4. es6基础系列一:let和const

    let 声明变量,可以说是具有作用域的var,用于声明变量,主要规则如下: 1 let声明的变量拥有块级作用域 { let i = 1; console.log(i); // 1 } console. ...

  5. javascript核心概念之——数组

    在javascript中数组就是一个可以存放任何类型的集合.存储在数组中的值用逗号分隔 var arr = ["hello",7,null,undifined,obj,undifi ...

  6. 使用react native制作的一款网络音乐播放器

    使用react native制作的一款网络音乐播放器 基于第三方库 react-native-video设计"react-native-video": "^1.0.0&q ...

  7. spring项目log4j使用入门

    log4j是Java开发中经常使用的一个日志框架,功能强大,配置灵活,基本上可以满足项目开发中对日志功能的大部分需求.我前后经历了四五个项目,采用的日志框架都是log4j,这也反应了log4j受欢迎的 ...

  8. Android Weekly Notes Issue #253

    Android Weekly Issue #253 April 16th, 2017 Android Weely Issue #253. 本期内容包括: Android O新推出的自定义字体支持; 用 ...

  9. Javascript数组与基本函数

    数组定义方法: 1. var arr=new Array(); var arr=Array(); 2. var arr1=Array('a','b','c'); 3. var arr2=['a','b ...

  10. 10个漂亮的jQuery日历插件下载【转载】

    10个漂亮的jQuery日历插件下载 2013-08-07 标签:jQuery日历插件jQuery日历jQuery插件   日期是非常重要的,随时随地.微薄或网站的日期选取器日历必须在那里.您可以使用 ...