Java自定义注解

约定不等于承诺〃 2022-08-05 02:51 154阅读 0赞

首先声明,本文转自:http://www.itzhai.com/java-based-notebook-annotation-annotation-introduction-and-use-custom-annotations.html

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

1、元注解

元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四种。
1.1、@Retention: 定义注解的保留策略

@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含

@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

1.2、@Target:定义注解的作用目标

其定义的源码为:

复制代码

  1. 1 @Documented
  2. 2 @Retention(RetentionPolicy.RUNTIME)
  3. 3 @Target(ElementType.ANNOTATION_TYPE)
  4. 4 public @interface Target {
  5. 5 ElementType[] value();
  6. 6 }

复制代码

@Target(ElementType.TYPE) //接口、类、枚举、注解

@Target(ElementType.FIELD) //字段、枚举的常量

@Target(ElementType.METHOD) //方法

@Target(ElementType.PARAMETER) //方法参数

@Target(ElementType.CONSTRUCTOR) //构造函数

@Target(ElementType.LOCAL_VARIABLE)//局部变量

@Target(ElementType.ANNOTATION_TYPE)//注解

@Target(ElementType.PACKAGE) ///包

由以上的源码可以知道,他的elementType 可以有多个,一个注解可以为类的,方法的,字段的等等

1.3、@Document:说明该注解将被包含在javadoc中

1.4、@Inherited:说明子类可以继承父类中的该注解

2、java 注解的自定义

下面是自定义注解的一个例子

复制代码

  1. @Documented
  2. @Target({ElementType.TYPE,ElementType.METHOD})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface Yts {
  5. public enum YtsType{util,entity,service,model};
  6. public YtsType classType() default YtsType.util;
  7. }

复制代码

复制代码

  1. 1 @Documented
  2. 2 @Retention(RetentionPolicy.RUNTIME)
  3. 3 @Target(ElementType.METHOD)
  4. 4 @Inherited
  5. 5 public @interface HelloWorld {
  6. 6 public String name()default "";
  7. 7 }

复制代码

@Retention(RetentionPolicy.RUNTIME)

定义的这个注解是注解会在class字节码文件中存在,在运行时可以通过反射获取到。

@Target({ElementType.TYPE,ElementType.METHOD})

因此这个注解可以是类注解,也可以是方法的注解

这样一个注解就自定义好了,当然注解里面的成员可以为基本的数据类型,也可以为数据,Object等等

3 注解是定义好了,那么怎么来得到,解析注解呢?

java的反射机制可以帮助,得到注解,代码如下:

复制代码

  1. 1 public class ParseAnnotation {
  2. 2
  3. 3 public void parseMethod(Class clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException{
  4. 4 Object obj = clazz.getConstructor(new Class[]{}).newInstance(new Object[]{});
  5. 5 for(Method method : clazz.getDeclaredMethods()){
  6. 6 HelloWorld say = method.getAnnotation(HelloWorld.class);
  7. 7 String name = "";
  8. 8 if(say != null){
  9. 9 name = say.name();
  10. 10 method.invoke(obj, name);
  11. 11 }
  12. 12 Yts yts = (Yts)method.getAnnotation(Yts.class);
  13. 13 if(yts != null){
  14. 14 if(YtsType.util.equals(yts.classType())){
  15. 15 System.out.println("this is a util method");
  16. 16 }else{
  17. 17 System.out.println("this is a other method");
  18. 18 }
  19. 19 }
  20. 20 }
  21. 21 }
  22. 22 @SuppressWarnings("unchecked")
  23. 23 public void parseType(Class clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
  24. 24 Yts yts = (Yts) clazz.getAnnotation(Yts.class);
  25. 25 if(yts != null){
  26. 26 if(YtsType.util.equals(yts.classType())){
  27. 27 System.out.println("this is a util class");
  28. 28 }else{
  29. 29 System.out.println("this is a other class");
  30. 30 }
  31. 31 }
  32. 32 }
  33. 33
  34. 34 }

复制代码

前一个方法是解析得到方法注解的,后一个方法是得到类注解的

以下是测试方法类

复制代码

  1. 1 @Yts(classType =YtsType.util)
  2. 2 public class SayHell {
  3. 3
  4. 4 @HelloWorld(name = " 小明 ")
  5. 5 @Yts
  6. 6 public void sayHello(String name){
  7. 7 if(name == null || name.equals("")){
  8. 8 System.out.println("hello world!");
  9. 9 }else{
  10. 10 System.out.println(name + "say hello world!");
  11. 11 }
  12. 12 }
  13. 13 }

复制代码

复制代码

  1. 1 public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException {
  2. 2 ParseAnnotation parse = new ParseAnnotation();
  3. 3 parse.parseMethod(SayHell.class);
  4. 4 parse.parseType(SayHell.class);
  5. 5 }

复制代码

http://blog.csdn.net/yixiaogang109/article/details/7328466

1、Annotation的工作原理:

JDK5.0中提供了注解的功能,允许开发者定义和使用自己的注解类型。该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具组成。

Annotation并不直接影响代码的语义,但是他可以被看做是程序的工具或者类库。它会反过来对正在运行的程序语义有所影响。

Annotation可以冲源文件、class文件或者在运行时通过反射机制多种方式被读取。

2、@Override注解:

  1. java.lang
  2. 注释类型 Override
  3. @Target(value=METHOD)
  4. @Retention(value=SOURCE)
  5. public @interface Override

表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。

@Override注解表示子类要重写父类的对应方法。

Override是一个Marker annotation,用于标识的Annotation,Annotation名称本身表示了要给工具程序的信息。

下面是一个使用@Override注解的例子:

  1. class A {
  2. private String id;
  3. A(String id){
  4. this.id = id;
  5. }
  6. @Override
  7. public String toString() {
  8. return id;
  9. }
  10. }

3、@Deprecated注解:

  1. java.lang
  2. 注释类型 Deprecated
  3. @Documented
  4. @Retention(value=RUNTIME)
  5. public @interface Deprecated

用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。

@Deprecated注解表示方法是不被建议使用的。

Deprecated是一个Marker annotation。

下面是一个使用@Deprecated注解的例子:

  1. class A {
  2. private String id;
  3. A(String id){
  4. this.id = id;
  5. }
  6. @Deprecated
  7. public void execute(){
  8. System.out.println(id);
  9. }
  10. public static void main(String[] args) {
  11. A a = new A("a123");
  12. a.execute();
  13. }
  14. }

4、@SuppressWarnings注解:

  1. java.lang
  2. 注释类型 SuppressWarnings
  3. @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
  4. @Retention(value=SOURCE)
  5. public @interface SuppressWarnings

指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。注意,在给定元素中取消显示的警告集是所有包含元素中取消显示的警告的超集。例如,如果注释一个类来取消显示某个警告,同时注释一个方法来取消显示另一个警告,那么将在此方法中同时取消显示这两个警告。

根据风格不同,程序员应该始终在最里层的嵌套元素上使用此注释,在那里使用才有效。如果要在特定的方法中取消显示某个警告,则应该注释该方法而不是注释它的类。

@SuppressWarnings注解表示抑制警告。

下面是一个使用@SuppressWarnings注解的例子:

  1. @SuppressWarnings("unchecked")
  2. public static void main(String[] args) {
  3. List list = new ArrayList();
  4. list.add("abc");
  5. }

5、自定义注解:

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。

自定义最简单的注解:

  1. public @interface MyAnnotation {
  2. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation
  3. public void execute(){
  4. System.out.println("method");
  5. }
  6. }

5.1、添加变量:

  1. public @interface MyAnnotation {
  2. String value1();
  3. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation(value1="abc")
  3. public void execute(){
  4. System.out.println("method");
  5. }
  6. }

当注解中使用的属性名为value时,对其赋值时可以不指定属性的名称而直接写上属性值接口;除了value意外的变量名都需要使用name=value的方式赋值。

5.2、添加默认值:

  1. public @interface MyAnnotation {
  2. String value1() default "abc";
  3. }

5.3、多变量使用枚举:

  1. public @interface MyAnnotation {
  2. String value1() default "abc";
  3. MyEnum value2() default MyEnum.Sunny;
  4. }
  5. enum MyEnum{
  6. Sunny,Rainy
  7. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation(value1="a", value2=MyEnum.Sunny)
  3. public void execute(){
  4. System.out.println("method");
  5. }
  6. }

5.4、数组变量:

  1. public @interface MyAnnotation {
  2. String[] value1() default "abc";
  3. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation(value1={"a","b"})
  3. public void execute(){
  4. System.out.println("method");
  5. }
  6. }

6、设置注解的作用范围:

  1. @Documented
  2. @Retention(value=RUNTIME)
  3. @Target(value=ANNOTATION_TYPE)
  4. public @interface Retention

指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS。

只有元注释类型直接用于注释时,Target 元注释才有效。如果元注释类型用作另一种注释类型的成员,则无效。

  1. public enum RetentionPolicy
  2. extends Enum<RetentionPolicy>

注释保留策略。此枚举类型的常量描述保留注释的不同策略。它们与 Retention 元注释类型一起使用,以指定保留多长的注释。

  1. CLASS
  2. 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。
  3. RUNTIME
  4. 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
  5. SOURCE
  6. 编译器要丢弃的注释。

@Retention注解可以在定义注解时为编译程序提供注解的保留策略。

属于CLASS保留策略的注解有@SuppressWarnings,该注解信息不会存储于.class文件。

6.1、在自定义注解中的使用例子:

  1. @Retention(RetentionPolicy.CLASS)
  2. public @interface MyAnnotation {
  3. String[] value1() default "abc";
  4. }

7、使用反射读取RUNTIME保留策略的Annotation信息的例子:

  1. java.lang.reflect
  2. 接口 AnnotatedElement
  3. 所有已知实现类:
  4. AccessibleObject, Class, Constructor, Field, Method, Package

表示目前正在此 VM 中运行的程序的一个已注释元素。该接口允许反射性地读取注释。由此接口中的方法返回的所有注释都是不可变并且可序列化的。调用者可以修改已赋值数组枚举成员的访问器返回的数组;这不会对其他调用者返回的数组产生任何影响。

如果此接口中的方法返回的注释(直接或间接地)包含一个已赋值的 Class 成员,该成员引用了一个在此 VM 中不可访问的类,则试图通过在返回的注释上调用相关的类返回的方法来读取该类,将导致一个 TypeNotPresentException。

  1. isAnnotationPresent
  2. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。此方法主要是为了便于访问标记注释而设计的。

参数:

annotationClass – 对应于注释类型的 Class 对象

返回:

如果指定注释类型的注释存在于此对象上,则返回 true,否则返回 false

抛出:

NullPointerException – 如果给定的注释类为 null

从以下版本开始:

1.5

  1. getAnnotation
  2. <T extends Annotation> T getAnnotation(Class<T> annotationClass)

如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

参数:

annotationClass – 对应于注释类型的 Class 对象

返回:

如果该元素的指定注释类型的注释存在于此对象上,则返回这些注释,否则返回 null

抛出:

NullPointerException – 如果给定的注释类为 null

从以下版本开始:

1.5

  1. getAnnotations
  2. Annotation[] getAnnotations()

返回此元素上存在的所有注释。(如果此元素没有注释,则返回长度为零的数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

返回:

此元素上存在的所有注释

从以下版本开始:

1.5

  1. getDeclaredAnnotations
  2. Annotation[] getDeclaredAnnotations()

返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

返回:

直接存在于此元素上的所有注释

从以下版本开始:

1.5

下面是使用反射读取RUNTIME保留策略的Annotation信息的例子:

自定义注解:

  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface MyAnnotation {
  3. String[] value1() default "abc";
  4. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation(value1={"a","b"})
  3. @Deprecated
  4. public void execute(){
  5. System.out.println("method");
  6. }
  7. }

读取注解中的信息:

  1. public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
  2. AnnotationTest2 annotationTest2 = new AnnotationTest2();
  3. //获取AnnotationTest2的Class实例
  4. Class<AnnotationTest2> c = AnnotationTest2.class;
  5. //获取需要处理的方法Method实例
  6. Method method = c.getMethod("execute", new Class[]{});
  7. //判断该方法是否包含MyAnnotation注解
  8. if(method.isAnnotationPresent(MyAnnotation.class)){
  9. //获取该方法的MyAnnotation注解实例
  10. MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
  11. //执行该方法
  12. method.invoke(annotationTest2, new Object[]{});
  13. //获取myAnnotation
  14. String[] value1 = myAnnotation.value1();
  15. System.out.println(value1[0]);
  16. }
  17. //获取方法上的所有注解
  18. Annotation[] annotations = method.getAnnotations();
  19. for(Annotation annotation : annotations){
  20. System.out.println(annotation);
  21. }
  22. }

8、限定注解的使用:

限定注解使用@Target。

  1. @Documented
  2. @Retention(value=RUNTIME)
  3. @Target(value=ANNOTATION_TYPE)
  4. public @interface Target

指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。 例如,此元注释指示该声明类型是其自身,即元注释类型。它只能用在注释类型声明上:

  1. @Target(ElementType.ANNOTATION_TYPE)
  2. public @interface MetaAnnotationType {
  3. ...
  4. }

此元注释指示该声明类型只可作为复杂注释类型声明中的成员类型使用。它不能直接用于注释:

  1. @Target({})
  2. public @interface MemberType {
  3. ...
  4. }

这是一个编译时错误,它表明一个 ElementType 常量在 Target 注释中出现了不只一次。例如,以下元注释是非法的:

  1. @Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
  2. public @interface Bogus {
  3. ...
  4. }
  5. public enum ElementType
  6. extends Enum<ElementType>

程序元素类型。此枚举类型的常量提供了 Java 程序中声明的元素的简单分类。

这些常量与 Target 元注释类型一起使用,以指定在什么情况下使用注释类型是合法的。

  1. ANNOTATION_TYPE
  2. 注释类型声明
  3. CONSTRUCTOR
  4. 构造方法声明
  5. FIELD
  6. 字段声明(包括枚举常量)
  7. LOCAL_VARIABLE
  8. 局部变量声明
  9. METHOD
  10. 方法声明
  11. PACKAGE
  12. 包声明
  13. PARAMETER
  14. 参数声明
  15. TYPE
  16. 类、接口(包括注释类型)或枚举声明

注解的使用限定的例子:

  1. @Target(ElementType.METHOD)
  2. public @interface MyAnnotation {
  3. String[] value1() default "abc";
  4. }

9、在帮助文档中加入注解:

要想在制作JavaDoc文件的同时将注解信息加入到API文件中,可以使用java.lang.annotation.Documented。

在自定义注解中声明构建注解文档:

  1. @Documented
  2. public @interface MyAnnotation {
  3. String[] value1() default "abc";
  4. }

使用自定义注解:

  1. public class AnnotationTest2 {
  2. @MyAnnotation(value1={"a","b"})
  3. public void execute(){
  4. System.out.println("method");
  5. }
  6. }

10、在注解中使用继承:

默认情况下注解并不会被继承到子类中,可以在自定义注解时加上java.lang.annotation.Inherited注解声明使用继承。

  1. @Documented
  2. @Retention(value=RUNTIME)
  3. @Target(value=ANNOTATION_TYPE)
  4. public @interface Inherited

指示注释类型被自动继承。如果在注释类型声明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。此过程会重复进行,直到找到此类型的注释或到达了该类层次结构的顶层 (Object) 为止。如果没有超类具有该类型的注释,则查询将指示当前类没有这样的注释。

注意,如果使用注释类型注释类以外的任何事物,此元注释类型都是无效的。还要注意,此元注释仅促成从超类继承注释;对已实现接口的注释无效。

发表评论

表情:
评论列表 (有 0 条评论,154人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java定义注解

    简介:     注解是在JDK5.0及以后版本中引入的。他可以用于创建文档,跟踪代码中的依赖性,甚至执行基本的编译时检查,而且还可以作为在spring框架中进行注解式注入Be

    相关 Java定义注解

    说到注解,也是面试中的重点高频知识点,因为利用注解加上反射可以做很多事情,通过注解对一些类或方法进行标示(override),通过注解、反射和AOP切面来做缓存,通过注解在类文