1、注解的概念

(1)注解的定义
注解是一种能被添加到Java代码中的元数据,类、方法、变量、参数和包都可以用注解修饰。注解对于它修饰的代码并没有直接影响。

(2)注解的使用范围
为编译器提供信息。
编译器和部署时的处理。
运行时的处理。

2、注解的语法

(1)声明部分

public @interface MyAnnotation {
}

使用@interface声明为注解类型;在底层,所有注解都会自动继承java.lang.annotation.Annotation接口。
(2)实现部分

public @interface MyAnnotation {
	public String name();
	int age() default 18;
	int[] array();
}

注解内部实现定义的是注解类型元素。
定义注解类型元素有以下要求:

  1. 访问修饰符必须为public,不写默认为public;
  2. 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(注解可以相互嵌套)以及以上数据的数组;
  3. 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value,更加便于使用;
  4. 注解中的()不能定义任何参数,定义的也并非是方法,只是语法要求;
  5. default为默认值,与对应元素类型一致;
  6. 若没有默认值,代表使用注解时,必须对该类型元素进行赋值。

3、元注解

元注解:专门定义注解的注解。它们是为了实现自定义注解而专门是设计的。
(1)@Target
该注解定义自定义注解能够使用在哪些元素上。
该类型元素为一个枚举类型,定义为java.lang.annotation.ElementType。

public enum ElementType {
    /** 类,接口(包括注解)或枚举声明 */
    TYPE,
    /** 属性或枚举常量的声明 */
    FIELD,
    /** 方法声明 */
    METHOD,
    /** 方法参数声明 */
    PARAMETER,
    /** 构造函数声明 */
    CONSTRUCTOR,
    /** 局部变量声明 */
    LOCAL_VARIABLE,
    /** 注解类型声明 */
    ANNOTATION_TYPE,
    /** 包声明,只能用在package-info.java文件中*/
    PACKAGE,
    /**
     * 类型参数声明,即泛型方法、泛型类、泛型接口
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * 可以声明任意类型除了Class
     * @since 1.8
     */
    TYPE_USE
}

(2)@Retention
该注解定义自定义注解的声明周期。该类型元素为java.lang.annotation.RetentionPolicy。

public enum RetentionPolicy {
    /**
     * Java源文件阶段,注解会被编译器忽略。
     */
    SOURCE,
    /**
	 * 编译到Class文件阶段,注解将被编译到class中,但在运行时会被虚拟机忽略。
     */
    CLASS,

    /**
     * 运行阶段,不仅会编译到class文件中,还会被虚拟机识别,通过反射进行读取。
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}
  1. 如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
  2. 如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解进行处理,但是运行时JVM(Java虚拟机)会忽略它,在运行期也不能读取到;
  3. 如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME;
  4. 在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS。

(3)@Documented
该注解是用来被指定自定义注解是否随被定义的Java文件生成到JavaDoc文档中。
(4)@Inherited
该注解指定自定义注解能够被子类继承。该注解只能针对@Target(ElementType.TYPE)的自定义注解起作用。

4、特殊语法

(1)若注解没有注解类型元素,那么是可以省略()
例如:@Documented()等价于@Documented
(2)若注解本身只有一个注解类型元素,而且命名为value,在使用注解时可以直接使用@注解名(注解值)
例如:@Retention(value = RetentionPolicy.RUNTIME)等价于@Retention(RetentionPolicy.RUNTIME)
(3)若注解中某个注解类型元素是一个数组类型,在使用时又只需要填入一个值,那么可以直接使用@注解名(类型名 = 类型值)
例如:@Target(value = {ElementType.TYPE})等价于@Target(value = ElementType.TYPE)等价于@Target(ElementType.TYPE)

5、使用自定义注解

共分为三个阶段
1.定义注解
2.配置注解
3.解析注解
(1)定义注解
使用上面介绍的元注解,定义我们想要的自定义注解
//代码

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldAnnotation {
    //判断字段是否为空
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotation {
    String value() default "this is a method";
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotation {
    //获取参数的Class
}
@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented()
public @interface TypeAnnotation {
    public String desc() default "this a class";
}

(2)配置注解
根据要只用的自定义注解,我们把它配置在合适的位置上。

@TypeAnnotation
public class Demo {
    @FieldAnnotation
    private String name;

    private int size;

    @MethodAnnotation(value = "getSomeThing...")
    public void getSomeThing(@ParameterAnnotation String param) {
        System.out.println("getSomeThing");
    }

    public void saySomeThing(String param) {
        System.out.println("saySomeThing");
    }
}

(3)解析注解
通过反射获取在已配置的注解和其注解类型元素配置的值。

public class Client {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Class clz = demo.getClass();
        TypeAnnotation typeAnnotation = (TypeAnnotation) clz.getAnnotation(TypeAnnotation.class);
        System.out.println(typeAnnotation.desc());

        Method[] methods = clz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MethodAnnotation.class)) {
                MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
                System.out.println(methodAnnotation.value());
            }
            method.getParameterAnnotations();
        }

        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(FieldAnnotation.class)) {
                FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
                System.out.println(field.getName());
            }
        }
    }
}

有关获取注解的反射方法介绍。

  1. isAnnotationPresent(Class<? extends Annotation> annotationClass),该方法判断该元素上是否配置某个指定注解。
  2. getAnnotation(Class< T > annotationClass),该方法获取元素上指定的注解,随之可以获取到注解类型元素上配置的值。
  3. getAnnotations(),该方法获取元素上配置的所有注解,返回的为注解数组。

样例:通过自定义注解导出Excel

1、定义注解

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name();
    int width();
    //TODO 对字段进行排序
    int sort() default 0;
}

2、配置注解

public class Person {
    @Column(name = "姓名",width = 15)
    private String name;
    @Column(name = "年龄",width = 10)
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

3、解析注解

public class ExcelUtils {
    private final static float CORRECTION_PARAMETER = 0.72F;

    public static <T extends Object> void exportExcel(List<T> data, Class<T> clz) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, IOException {
        //1.创建一个webbook,对应一个Excel文件
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet("Sheet1");
        //2.第一行表头
        HSSFRow headRow = sheet.createRow(0);
        Field[] fields = clz.getDeclaredFields();
        //设置表头格式
        HSSFCellStyle headCellStyle = workbook.createCellStyle();
        Font fontStyle = workbook.createFont();
        fontStyle.setBold(true);
//        fontStyle.setFontHeightInPoints((short) 12);
        headCellStyle.setFont(fontStyle);
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                Cell cell = headRow.createCell(i);
                cell.setCellValue(column.name());
                cell.setCellStyle(headCellStyle);
                sheet.setColumnWidth(i, (int) ((column.width() + CORRECTION_PARAMETER) * 256));
            }

        }
        //3.第二行开始遍历数据
        for (int i = 0; i < data.size(); i++) {
            HSSFRow row = sheet.createRow(i + 1);
            //逐列填充
            for (int j = 0; j < fields.length; j++) {
                Object object = data.get(i);
                Field field = fields[j];
                if (field.isAnnotationPresent(Column.class)) {
                    String fieldName = field.getName();
                    String firstLetter = fieldName.substring(0, 1).toUpperCase();
                    String getter = "get" + firstLetter + fieldName.substring(1);
                    Method method = object.getClass().getMethod(getter, new Class[]{});
                    Object value = method.invoke(object, new Object[]{});
                    row.createCell(j).setCellValue(value.toString());
                }

            }
        }
        workbook.write(new FileOutputStream("D:/log/table.xls"));
    }
}