欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

注解-Annotation Types学习

程序员文章站 2022-03-20 18:04:35
...

前言:注解是JDK5 之后推出的特性,可修饰包、类、方法、变量等,通过在编译、加载和运行时读取其信息,可执行相应的处理。

基本原理

java注解类型实质上是一个标记,如:

@Autowired
private TestBean testBean;

@Autowired标记了testBean这个域属性,这个标记的意义就是自动注入,告诉spring这个域属性需要注入该类型的对象。

通过反射机制,可以得到相应的注解,并对注解进行定制化处理,如@Deprecated 标记过期,@Override 标记重写函数。
注解本身有无,并不影响程序的编译如@Override,但可能会影响程序的正常运行,如@Autowired,若删掉该注解,则对应域属性没有实例化的bean,产生异常。

注解组成

注解由来

public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

注解不是新的东西,也是需要通过java原生语言来定义,Annotation接口声明了注解的源头。在定义注解时,仅需要在定义接口时,在interface 前加 @ 即可定义成功。@interface 实际上继承了 java.lang.annotation.Annotation 表明自己为注解类型。

注:注解本身不能再被继承,且无法通过extends java.lang.annotation.Annotation 方式声明自己的注解类型

以@Override为例,对注解的定义进行讲解。@Override 表明该方法覆盖了其父类或接口的方法。其上又包含了@Target与@Retention两个注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  • @Target注解表明该注解可标注的位置,其内部接受一个ElementType类型的数组,ElementType类型如下:
public enum ElementType {

    /** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
    TYPE,
    /** 标明该注解可以用于字段(域)声明,包括enum实例 */
    FIELD,

    /** 标明该注解可以用于方法声明 */
    METHOD,

    /** 标明该注解可以用于参数声明 */
    PARAMETER,

    /** 标明注解可以用于构造函数声明 */
    CONSTRUCTOR,

    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,

    /** 标明注解可以用于注解声明(应用于另一个注解上)*/
    ANNOTATION_TYPE,

    /** 标明注解可以用于包声明 */
    PACKAGE,

    /**
     * 标明注解可以用于类型参数声明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 类型使用声明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
}
  • @Retention表明注解的生命周期,其接受RetentionPolicy枚举值,RetentionPolicy类型如下:
public enum RetentionPolicy {
    /**
     * 信息只保留在源码中,编译时将丢弃
     */
    SOURCE,

    /**
     * 信息只保留在源码和 编译后的class文件中,但加载到JVM时将被丢弃。
     */
    CLASS,

    /**
     * 信息将保留在源码、class文件以及运行时。
     */
    RUNTIME
}

再看这两个注解的定义:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

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

可见这两个注解自身也被自身标记,并声明了自身的声明周期及作用对象。

@Target和@Retention注解中,包含了value()方法,并含有返回值。该方法表明了注解接受何种类型的属性值。如@Target接受ElementType类型的数组,该属性值将由该注解定义者来决定如何使用。

其中,注解内可定义多个方法,且多种返回类型,但由限制。

  • 对于方法,若有属性值且无默认值,则必须提供该属性的值,如@Target(ElementType.ANNOTATION_TYPE)或定义默认值。
value() default ElementType.ANNOTATION_TYPE

当注解内有唯一的方法,且方法名为value() 则,在使用时,无须通过key=value形式,直接赋值即可,如@Target(ElementType.ANNOTATION_TYPE),否则需要通过@Target(value=ElementType.ANNOTATION_TYPE)赋值。

  • 对于返回值,需要在以下类型范围内,且不能接受null值。
所有基本类型(int,float,boolean,byte,double,char,long,short)

String

Class

enum

Annotation

上述类型的数组

public @interface AnnotationElementDemo {
    //枚举类型
    enum Status {FIXED,NORMAL};

    //声明枚举
    Status status() default Status.FIXED;

    //布尔类型
    boolean showSupport() default false;

    //String类型
    String name()default "";

    //class类型
    Class<?> testCase() default Void.class;

    //注解嵌套
    Override reference();

    //数组类型
    long[] value();
}

注解信息使用

返回值 方法名 说明
<A extends Annotation> getAnnotation(Class<A>annotationClass) 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations() 返回此元素上存在的所有注解,包括从父类继承的
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组

简单实例:

package com.zejian.annotationdemo;

import java.lang.annotation.Annotation;
import java.util.Arrays;

/**
 * Created by zejian on 2017/5/20.
 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
 */
@DocumentA
class A{ }

//继承了A类
@DocumentB
public class DocumentDemo extends A{

    public static void main(String... args){

        Class<?> clazz = DocumentDemo.class;
        //根据指定注解类型获取该注解
        DocumentA documentA=clazz.getAnnotation(DocumentA.class);
        System.out.println("A:"+documentA);

        //获取该元素上的所有注解,包含从父类继承
        Annotation[] an= clazz.getAnnotations();
        System.out.println("an:"+ Arrays.toString(an));
        //获取该元素上的所有注解,但不包含继承!
        Annotation[] an2=clazz.getDeclaredAnnotations();
        System.out.println("an2:"+ Arrays.toString(an2));

        //判断注解DocumentA是否在该元素上
        boolean b=clazz.isAnnotationPresent(DocumentA.class);
        System.out.println("b:"+b);

        /**
         * 执行结果:
         A:@com.zejian.annotationdemo.DocumentA()
         an:[@com.zejian.annotationdemo.DocumentA(), @com.zejian.annotationdemo.DocumentB()]
         an2:@com.zejian.annotationdemo.DocumentB()
         b:true
         */
    }
}

Demo

@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @ interface PersonalName {
    String filedName() default "";

    String methodName() default "";

    String className() default "";
}


@PersonalName(className = "测试类")
public class TestBean {

    @PersonalName(filedName = "我的名字")
    public String myName= "";

    @PersonalName(filedName = "你的名字")
    public String yourName="";

    @PersonalName(methodName = "打印A")
    public void printA(){
        System.out.println("A");
    }
}

public class JunitTest {

    private Class<?> test = TestBean.class;

    @Test
    public void testPrintFiled(){
        for(Field field:test.getDeclaredFields()){
            //getDeclaredAnnotations
            Annotation[] annotations = field.getDeclaredAnnotations();
            for (Annotation ann:annotations){
                if(ann instanceof PersonalName){
                    System.out.println("类:"+test.getName()+",属性:"+field.getName()+",自定义名:"+((PersonalName) ann).filedName());
                }
            }

            //getDeclaredAnnotation
            Annotation annotationFiled = field.getAnnotation(PersonalName.class);
            if(annotationFiled!=null){
                System.out.println("类:"+test.getName()+",属性:"+field.getName()+",自定义名:"+((PersonalName) annotationFiled).filedName());

            }
        }
    }

    @Test
    public void testMethod(){
        for(Method method:test.getDeclaredMethods()){
            Annotation annotationMethod = method.getAnnotation(PersonalName.class);
            if(annotationMethod!=null){
                System.out.println("类:"+test.getName()+",方法:"+method.getName()+",自定义名:"+((PersonalName) annotationMethod).methodName());
            }
        }
    }

    @Test
    public void testClass(){
        Annotation annotation = test.getAnnotation(PersonalName.class);
        TestBean testBean = null;
        if(annotation!=null){
            System.out.println("类:"+test.getName()+",自定义名:"+((PersonalName) annotation).className());
            try{
                testBean = (TestBean) test.newInstance();
                testBean.printA();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}