从零学习Java注解(一)

导读:本篇文章讲解 从零学习Java注解(一),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1、注解保留策略

public enum RetentionPolicy {
    /**
     * 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃.
     * 这意味着:Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了
     */
    SOURCE,
 
    /**
     * 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期.
     */
    CLASS,
 
    /**
     * 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,
     * 保存到class对象中,可以通过反射来获取
     */
    RUNTIME
}

2、注解能用到哪里

public enum ElementType {
    /**
     * 类、接口(包括注解类型)或枚举声明
     */
    TYPE,
    
    /**
     * 注解类型声明
     */
    ANNOTATION_TYPE,
    
    /**
     * 字段声明(包括枚举常量)
     */
    FIELD,
    
    /**
     * 方法声明
     */
    METHOD,
    
    /**
     * 参数声明
     */
    PARAMETER,
    
    /**
     * 构造方法声明
     */
    CONSTRUCTOR,
    
    /**
     * 局部变量声明
     */
    LOCAL_VARIABLE,

    /**
     * 包声明
     */
    PACKAGE
}

3、和注解有关的Java原生API

java.lang.Class<T>

public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。

Annotation[] getAnnotations()
返回此元素上存在的所有注解。

Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。

boolean isAnnotation()
如果此 Class 对象表示一个注解类型则返回 true。

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
java.lang.reflect.Constructor<T>

<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。

Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
java.lang.reflect.Field

<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
		  
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
java.lang.reflect.Method

<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。

Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。

Annotation[][] getParameterAnnotations()
返回表示按照声明顺序对此 Method 对象所表示方法的形参进行注解的那个数组的数组。

4、小试牛刀

该Demo参考了这篇博客
假设你想模仿org.junit.Test写一个自己的注解类。

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) // 该注解只能用在方法签名上面
public @interface Test {

  // 若值为false,表示使用该注解的那个方法将被忽略,即不想被执行
  boolean enabled() default true;
}

假设你还想自定义一个注解,用于标识测试人的一些信息。

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 该注解只能用在类、接口(包括注解类型)或枚举声明上面
public @interface TesterInfo {

  enum Priority {
    LOW, MEDIUM, HIGH
  }

  Priority priority() default Priority.MEDIUM;

  String[] tags() default "";

  String createdBy() default "Liu Dehua";

  String lastModified() default "2019-06-03 18:00:00";
}

展示上述两个自定义注解的用法。

@TesterInfo(
    priority = Priority.HIGH,
    createdBy = "Xiaoming",
    tags = {"sales","test" },
    lastModified = "2019-6-03 15:28:00"
)
public class TestExample {

  @Test
  void testA() {
    if (true)
      throw new RuntimeException("This test always failed");
  }

  /**
   * 通过enabled = false告知注解解释器不要执行
   */
  @Test(enabled = false)
  void testB() {
    if (false)
      throw new RuntimeException("This test always passed");
  }

  @Test(enabled = true)
  void testC() {
    if (10 > 1) {
      // 省略
    }
  }

}

无论注解设计的多么完美,总需要一个“懂它”的注解解释器。

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class AnnotationInterpreter {

  public static void main(String[] args) throws Exception {

    System.out.println("Testing...");

    int passed = 0, failed = 0, count = 0, ignore = 0;

    // 硬编码.class文件不太好,项目中更常见的是扫描某些包中的所有类
    Class<TestExample> obj = TestExample.class;

    // 处理TesterInfo注解
    if (obj.isAnnotationPresent(TesterInfo.class)) {

      Annotation annotation = obj.getAnnotation(TesterInfo.class);
      TesterInfo testerInfo = (TesterInfo) annotation;

      System.out.printf("%nPriority :%s", testerInfo.priority());
      System.out.printf("%nCreatedBy :%s", testerInfo.createdBy());
      System.out.printf("%nTags :");

      int tagLength = testerInfo.tags().length;
      for (String tag : testerInfo.tags()) {
        if (tagLength > 1) {
          System.out.print(tag + ", ");
        } else {
          System.out.print(tag);
        }
        tagLength--;
      }

      System.out.printf("%nLastModified :%s%n%n", testerInfo.lastModified());

    }

    // 处理自定义的Test注解,注意不是org.junit.Test
    for (Method method : obj.getDeclaredMethods()) {

      if (method.isAnnotationPresent(Test.class)) {

        Annotation annotation = method.getAnnotation(Test.class);
        Test test = (Test) annotation;

        if (test.enabled()) {
          try {
            method.invoke(obj.newInstance());
            System.out.printf("%s - Test '%s' - passed %n", ++count, method.getName());
            passed++;
          } catch (Throwable ex) {
            System.out.printf("%s - Test '%s' - failed: %s %n", ++count, method.getName(), ex.getCause());
            failed++;
          }
        } else {
          System.out.printf("%s - Test '%s' - ignored%n", ++count, method.getName());
          ignore++;
        }
      }
    }

    System.out.printf("%nResult : Total : %d, Passed: %d, Failed %d, Ignore %d%n", count, passed, failed, ignore);

  }
}

执行结果:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/17297.html

(0)
小半的头像小半

相关推荐

极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!