Java - 注解

in TCEHJava with 0 comment

什么是注解

  注解,可以看作是对 一个 类/方法 的一个扩展的模版,每个 类/方法 按照注解类中的规则,来为 类/方法 注解不同的参数,在用到的地方可以得到不同的 类/方法 中注解的各种参数与值
也就是说注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。

系统注解

  系统注解 含义
@Override 用于修饰方法,表示此方法重写了父类方法
@Deprecated 用于修饰方法,表示此方法已经过时
@SuppressWarnnings 该注解用于告诉编译器忽视某类编译警告

元注解

元注解就是定义注解的注解,是java提供给我们用于定义注解的基本注解.在java.lang.annotation包中我们可以看到目前元注解共有以下几个:

@Retention

注解保留策略 含义
@Retention(RetentionPolicy.SOURCE) 注解仅在源码中保留,class文件中不存在
@Retention(RetentionPolicy.CLASS) 注解在源码和class文件中都存在,但运行时不存在,即运行时无法获得,该策略也是默认的保留策略
@Retention(RetentionPolicy.RUNTIME) 注解在源码,class文件中存在且运行时可以通过反射机制获取到

@Target

作用目标 含义
@Target(ElementType.TYPE) 用于接口(注解本质上也是接口),类,枚举
@Target(ElementType.FIELD) 用于字段,枚举常量
@Target(ElementType.METHOD) 用于方法
@Target(ElementType.PARAMETER) 用于方法参数
@Target(ElementType.CONSTRUCTOR) 用于构造参数
@Target(ElementType.LOCAL_VARIABLE) 用于局部变量
@Target(ElementType.ANNOTATION_TYPE) 用于注解
@Target(ElementType.PACKAGE) 用于包

@Inherited
默认情况下,我们自定义的注解用在父类上不会被子类所继承.如果想让子类也继承父类的注解,即注解在子类也生效,需要在自定义注解时设置@Inherited.一般情况下该注解用的比较少.

@Documented
该注解用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中.
比如使用该注解的@Target会出现在api说明中.

自定义注解

定义注解

package com.back.annotation;

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

//@Inherited 加上该注解,即允许子类继承该注解
//@Documented 加上该注解,则包含在 JavaDoc 文档中
@Target(value = { ElementType.FIELD,ElementType.TYPE }) // 作用类型。如属性,方法,类等等
@Retention(value = RetentionPolicy.RUNTIME) // 保留阶段。如源码.Java,字节码.class,运行时JVM.
public @interface MyAnnotation {
    // 设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分
    // default 即默认值
    public String value() default "";

}

使用注解

package com.back.annotation;

@MyAnnotation("注解值")
public class MyAnnotationTest {
    public static void main(String[] args) {
        //通过反射获取注解
        MyAnnotation annotation = MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.value());//输出 注解值
    }

}

注意事项

此处只能使用public或者默认的defalt两个权限修饰符

配置参数的类型只能使用基本类型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation,及其对应的数组。

对于只含有一个配置参数的注解,参数名建议设置中value,即方法名为value

配置参数一旦设置,其参数值必须有确定的值,要不在使用注解的时候指定,要不在定义注解的时候使用default为其设置默认值,对于非基本类型的参数值来说,其不能为null.

注解处理器

   上面我们已经学会了如何定义注解,要想注解发挥实际作用,需要我们为注解编写相应的注解处理器.根据注解的特性,注解处理器可以分为运行时注解处理编译时注解处理器.运行时处理器需要借助反射机制实现,而编译时处理器则需要借助APT来实现.

无论是运行时注解处理器还是编译时注解处理器,主要工作都是读取注解及处理特定注解,从这个角度来看注解处理器还是非常容易理解的.

先来看看如何编写运行时注解处理器.

运行时注解处理器

熟悉java反射机制的同学一定对java.lang.reflect包非常熟悉,该包中的所有api都支持读取运行时Annotation的能力,即属性为@Retention(RetentionPolicy.RUNTIME)的注解.

在java.lang.reflect中的AnnotatedElement接口是所有程序元素的(Class,Method)父接口,我们可以通过反射获取到某个类的AnnotatedElement对象,进而可以通过该对象提供的方法访问Annotation信息,常用的方法如下:

方法 含义
<T extends Annotation> T getAnnotation(Class<T> annotationClass) 返回该元素上存在的制定类型的注解
Annotation[] getAnnotations() 返回该元素上存在的所有注解
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) 返回该元素指定类型的注解
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) 返回直接存在与该元素上的所有注释
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) 返回直接存在该元素岸上某类型的注释
Annotation[] getDeclaredAnnotations() 返回直接存在与该元素上的所有注释

示例
1、先定义一个注解

package com.back.annotation;

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

@Target(value = ElementType.CONSTRUCTOR)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
    int age() default -1;

    String name() default "";

    String sex() default "男";

}

2、定义实体

package com.back.entity;

import com.back.annotation.UserAnnotation;

/**
 * 用户实体
 * 
 * @author Back
 *
 */
public class User {
    private int age;
    private String name;
    private String sex;

    @UserAnnotation(age = 18, name = "小仙女", sex = "女")
    public User() {

    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User [age=" + age + ", name=" + name + ", sex=" + sex + "]";
    }

}

3、定义处理器

package com.back.annotation;

import java.lang.reflect.Constructor;

import com.back.entity.User;

public class AnnotationProcessor {

    public static void init(Object object) {
        // 非User类的对象。则抛出错误。
        if (!(object instanceof User)) {
            throw new IllegalArgumentException("[" + object.getClass().getSimpleName() + "] isn't type of User");
        }
        // 返回Object类的所有构造方法
        Constructor<?>[] declaredConstructors = object.getClass().getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            // 判断该构造方法是否存在指定的注解
            if (constructor.isAnnotationPresent(UserAnnotation.class)) {
                // 获取注解对象
                UserAnnotation annotation = constructor.getAnnotation(UserAnnotation.class);
                // 获取注解值
                int age = annotation.age();
                String sex = annotation.sex();
                String name = annotation.name();
                // 为被注解对象赋值
                User user = (User) object;
                user.setAge(age);
                user.setName(name);
                user.setSex(sex);
            }
        }

    }

}

4、运行结果

package com.back.annotation;

import com.back.entity.User;

public class UserAnnotationTest {
    public static void main(String[] args) {
        User user = new User();
        AnnotationProcessor.init(user);
        System.out.println(user.toString());
        // 输出结果 User [age=18, name=小仙女, sex=女]

    }

}
Comments are closed.