Annotation注解

https://www.jianshu.com/p/9471d6bcf4cf

什么是注解

  1. 提供信息给编译器: 编译器可以利用注解来检测出错误或者警告信息,打印出日志。
  2. 编译阶段时的处理: 软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理。
  3. 运行时处理: 某些注解可以在程序运行的时候接受代码的提取,自动做相应的操作。
  4. 正如官方文档的那句话所说,注解能够提供元数据,转账例子中处理获取注解值的过程是我们开发者直接写的注解提取逻辑,处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。上面转账例子中的processAnnotationMoney方法就可以理解为APT工具类。

注解的定义

  1. public @interface MyTestAnnotation {
  2. }

元注解

元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。元注解分别有@Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五种。

@Retention

  1. Retention英文意思有保留、保持的意思,它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行)。在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期
  2. @Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含
  3. @Retention(RetentionPolicy.CLASS), 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
  4. @Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取到
  5. 如果我们是自定义注解,则通过前面分析,我们自定义注解如果只存着源码中或者字节码文件中就无法发挥作用,而在运行期间能获取到注解才能实现我们目的,所以自定义注解中肯定是使用 @Retention(RetentionPolicy.RUNTIME)

@Target

  1. Target的英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方法参数变量等,同样也是通过枚举类ElementType表达作用类型
  2. @Target(ElementType.TYPE) 作用接口、类、枚举、注解
  3. @Target(ElementType.FIELD) 作用属性字段、枚举的常量
  4. @Target(ElementType.METHOD) 作用方法
  5. @Target(ElementType.PARAMETER) 作用方法参数
  6. @Target(ElementType.CONSTRUCTOR) 作用构造函数
  7. @Target(ElementType.LOCAL_VARIABLE)作用局部变量
  8. @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
  9. @Target(ElementType.PACKAGE) 作用于包
  10. @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
  11. @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
  12. 一般比较常用的是ElementType.TYPE类型

@Documented

Document的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Inherited

Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。

@Repeatable

Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。

  1. /**一个人喜欢玩游戏,他喜欢玩英雄联盟,绝地求生,极品飞车,尘埃4等,则我们需要定义一个人的注解,他属性代表喜欢玩游戏集合,一个游戏注解,游戏属性代表游戏名称*/
  2. /**玩家注解*/
  3. @Documented
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Target(ElementType.TYPE)
  6. public @interface People {
  7. Game[] value() ;
  8. }
  9. /**游戏注解*/
  10. @Repeatable(People.class)
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Target(ElementType.TYPE)
  13. public @interface Game {
  14. String value() default "";
  15. }
  16. /**玩游戏类*/
  17. @Game(value = "LOL")
  18. @Game(value = "PUBG")
  19. @Game(value = "NFS")
  20. @Game(value = "Dirt4")
  21. public class PlayGame {
  22. }

注解的属性

注解的属性其实和类中定义的变量有异曲同工之处,只是注解中的变量都是成员变量(属性),并且注解中是没有方法的,只有成员变量,变量名就是使用注解括号中对应的参数名,变量返回值注解括号中对应参数类型。相信这会你应该会对上面的例子有一个更深的认识。而@Repeatable注解中的变量则类型则是对应Annotation(接口)的泛型Class。

  1. /**注解Repeatable源码*/
  2. @Documented
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Target(ElementType.ANNOTATION_TYPE)
  5. public @interface Repeatable {
  6. /**
  7. * Indicates the <em>containing annotation type</em> for the
  8. * repeatable annotation type.
  9. * @return the containing annotation type
  10. */
  11. Class<? extends Annotation> value();
  12. }

注解的本质

注解的本质就是一个Annotation接口

  1. /**Annotation接口源码*/
  2. public interface Annotation {
  3. boolean equals(Object obj);
  4. int hashCode();
  5. Class<? extends Annotation> annotationType();
  6. }

注解成员变量赋值

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
    String name() default "mao";
    int age() default 18;
}

@MyTestAnnotation(name = "father",age = 50)
public class Father {
}

使用反射获取注解的信息

//---------------------MyFiled注解-----------------------
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 张辉
 * @Description MyFiled注解
 * @create 2020-06-27 10:30
 */
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFiled {
    String columnName();
    String type();
    int length();
}

//---------------------MyTable注解-----------------------
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 张辉
 * @Description MyTable注解
 * @create 2020-06-27 10:16
 */
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {
    String value();
}


//---------------------使用注解-----------------------
/**
 * @author 张辉
 * @Description
 * @create 2020-06-27 10:15
 */
@MyTable(value = "tb_student")
public class MyStudent {
    @MyFiled(columnName = "id",type = "int",length = 3)
    private int id;

    @MyFiled(columnName = "sname",type = "varchar",length = 10)
    private String studentName;

    @MyFiled(columnName = "age",type = "int",length = 10)
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

//---------------------使用反射获取注解-----------------------
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * @author 张辉
 * @Description 使用反射读取注解的信息,模拟处理注解信息的流程
 * @create 2020-06-27 10:36
 */
public class Demo03 {

    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("TestAnnotation.MyStudent");

            // 获得类的所有有效注解
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }

            // 获取类指定的注解
            MyTable mt = (MyTable) clazz.getAnnotation(MyTable.class);
            System.out.println(mt);

            // 获得类的属性的注解
            Field f = clazz.getDeclaredField("studentName");
            MyFiled myFiled = f.getAnnotation(MyFiled.class);
            System.out.println(myFiled.columnName() + "---" + myFiled.type() + "---" + myFiled.length());

            // 根据获取的表名、字段的信息,拼出DDL语句,然后使用JDBC 执行这个SQL,在数据库中生成相应的表。

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}