如何实现自定义注解.md

如果用最简单的话描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。
在Java中叫Annotation,Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。
Java自带的注解有@Override,@Supperwarning,@Deprecated

1.注解的作用

1.生成文档,比如@see @param @return
2.在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出
3.追踪代码依赖性,实现替代配置文件,比如Spring的各种注解等

2.如何实现一个自定义注解

我们先写一个最简单的注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionDesc {
}

上面这个注解没有任何的属性定义,我们看下定义一个注解所需的元素。首先使用关键字“@interface” 表明这类是注解类;然后这类上加上注解“@Retention”,这是定义注解所必须的。

注解元素

@Retention是注解的注解,称为元注解。我们看一下@Retention的内部:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

我们看到Retention本身也被@Retention注解,还有@Documented和@Target(ElementType.ANNOTATION_TYPE),ANNOTATION_TYPE表明这个注解应应用在注解上。
Retention有一个属性value,类型为枚举的RetentionPolicy,它没有默认值,所以不写这个值就会报错,我们继续看一下RetentionPolicy:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

这个枚举定义了三个值,SOURCE,CLASS,RUNTIME。分别定义我们写的注解保持到哪个生命阶段。把上面的英文注释翻译过来:

  • RetentionPolicy.SOURCE 注解的信息会被编译器抛弃,不会留在class文件中
  • RetentionPolicy.CLASS 注解将由编译器记录在类文件(.class)中 但不需要在VM在运行时保留。 这是默认值行为
  • RetentionPolicy.RUNTIME 注解的信息被保留在.class文件中当程序编译时,会被虚拟机保留在运行时

如此,我们实现并解释了最简单的注解,那么注解怎么起作用呢,我们借测试来说明下:

public class AnnotationTest {

    @FunctionDesc
    public void work(){}

    public static void main(String[] args) {
        try {
            Method workMethod = AnnotationTest.class.getMethod("work",null);
            if(workMethod.isAnnotationPresent(FunctionDesc.class)){
                System.out.println(workMethod.getAnnotation(FunctionDesc.class));
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

运行上面的代码,会输出下面这句:
@com.wy.core.annotation.FunctionDesc()
这表明我们的注解起作用了。细心的你会发现,注解的真正起作用是借助于“反射”完成的,这个我们待会深讲。

3.注解中的属性

上面我们定义的@FunctionDesc是没有任何功能的。实际上注解的功能实现是通过定义属性完成的。我们给FunctionDesc加上属性:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface FunctionDesc {
    String desc();
    int paramCount() default 0;
    String returnDesc() default "";
}

public class AnnotationTest {

    @FunctionDesc(desc = "干活")
    public void work(){}
    public static void main(String[] args) {
        try {
            Method workMethod = AnnotationTest.class.getMethod("work",null);
            if(workMethod.isAnnotationPresent(FunctionDesc.class)){
                FunctionDesc fd = workMethod.getAnnotation(FunctionDesc.class);
                System.out.println(fd);
                System.out.println("desc="+fd.desc());
                System.out.println("paramCount="+fd.paramCount());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

运行代码后输出:
@com.wy.core.annotation.FunctionDesc(paramCount=0, returnDesc=, desc=干活)
desc=干活
paramCount=0

我们添加了@Target({ElementType.METHOD}),表明这个注解适用在方法上。内部定义了3个属性,desc是用作方法描述 ,paramCount表明参数个数,returnDesc是返回值描述。
paramCount和returnDesc有默认值,注解时可以不填值,但是desc必须写值。

上面这个测试的例子,我们从AnnotationTest.class 反射拿到了work方法的Method定义,从Method获取了它上面的注解信息。
这就是注解功能处理实现的大致思路。

4.反射得到注解的相关方法

我们知道反射的相关类有Class,Method,Field,Constructor等,它们都实现了AnnotatedElement接口。AnnotatedElement是注解处理器类库的核心元素。
我们看下AnnotatedElement的定义

public interface AnnotatedElement {
    default boolean isAnnotationPresent(Class annotationClass) {
        return getAnnotation(annotationClass) != null;
    }

     T getAnnotation(Class annotationClass);
    Annotation[] getAnnotations();
    default  T[] getAnnotationsByType(Class annotationClass) {
         /*
          * Definition of associated: directly or indirectly present OR
          * neither directly nor indirectly present AND the element is
          * a Class, the annotation type is inheritable, and the
          * annotation type is associated with the superclass of the
          * element.
          */
         T[] result = getDeclaredAnnotationsByType(annotationClass);

         if (result.length == 0 && // Neither directly nor indirectly present
             this instanceof Class && // the element is a class
             AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
             Class superClass = ((Class) this).getSuperclass();
             if (superClass != null) {
                 // Determine if the annotation is associated with the
                 // superclass
                 result = superClass.getAnnotationsByType(annotationClass);
             }
         }

         return result;
     }
    default  T getDeclaredAnnotation(Class annotationClass) {
         Objects.requireNonNull(annotationClass);
         // Loop over all directly-present annotations looking for a matching one
         for (Annotation annotation : getDeclaredAnnotations()) {
             if (annotationClass.equals(annotation.annotationType())) {
                 // More robust to do a dynamic cast at runtime instead
                 // of compile-time only.
                 return annotationClass.cast(annotation);
             }
         }
         return null;
     }
    default  T[] getDeclaredAnnotationsByType(Class annotationClass) {
        Objects.requireNonNull(annotationClass);
        return AnnotationSupport.
            getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
                                            collect(Collectors.toMap(Annotation::annotationType,
                                                                     Function.identity(),
                                                                     ((first,second) -> first),
                                                                     LinkedHashMap::new)),
                                            annotationClass);
    }
    Annotation[] getDeclaredAnnotations();
}

所以我们通过反射得到Class,Method,Field,Constructor之后,就可以通过以上方法获取注解信息了,最常用的以下几个方法
1.getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
2.getAnnotations():返回该程序元素上存在的所有注解。
3.isAnnotationPresent(Class annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
4.Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注解。该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

你可能感兴趣的:(Java)