自定义注解

我们常用@Override,@SuppressWarnings,@Service等注解,这些都是预定义的注解。注解就是某种注解类型的一种实例,我们可以把它用在某个类上进行标注。下面这张图解释注解都是什么?

image.png

上图可以看出注解大体分为三种:元注解,标记注解,一般注解;如果我们实现自定义的标记注解,就需要了解标准元注解和相关定义注解的语法。元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

  • @Target
  • @Retention
  • @Document
  • @Inherited

@Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。取值(ElementType):

  1. CONSTRUCTOR:用于描述构造器
  2. FIELD:用于描述符
  3. LOCAL_VARIABLE:用于描述局部变量
  4. METHOD:用于描述方法
  5. PACKAGE:用于描述包
  6. PARAMETER: 用于描述参数
  7. TYPE: 用于描述类、接口(包括注解类型)或者enum声明

@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。取值(RetentionPoicy):

  1. SOURCE:在源文件中有效(即源文件保留)
  2. CLASS:在class文件中有效(即class保留)
  3. RUNTIME:在运行时有效(即运行时保留)

@Documented

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented 是一个标记注解,没有成员。

@Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

自定义注解语法

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author chengaoxing
 * @date 2019年08月01日
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonAnnotation
{
    String name() default "test";
    int age();
}

上面是一个例子,定义了PersonAnnotation的注解,注解类用@interface修饰,有两个配置参数name和age。注解参数的可支持数据类型:

  1. 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  2. String类型
  3. Class类型
  4. enum类型
  5. Annotation类型
  6. 以上所有类型的数组

Annotation类型里面的参数的设定:

  1. 只能用public或默认(default)这两个访问权修饰.例如,String name();这里把方法设为defaul默认类型;
  2. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String name();这里的参数成员就为String;
  3. 如果只有一个参数成员,最好把参数名称设为"value",后加小括号

自定义注解使用

可以使用反射获取类的注解信息,下面是一个简单示例

import org.springframework.core.annotation.AnnotationUtils;

@PersonAnnotation(name = "11", age = 22)
public class TestAnnotation {

    public static void print(Class c) {
        System.out.println(c.getName());

        // java.lang.Class的getAnnotation方法,如果有注解,则返回注解。否则返回null
        PersonAnnotation person = AnnotationUtils.findAnnotation(c, PersonAnnotation.class);

        if (person != null) {
            System.out.println("name:" + person.name() + " age:" + person.age());
        } else {
            System.out.println("person unknown!");
        }
    }

    public static void main(String[] args) {
        TestAnnotation.print(TestAnnotation.class);
    }
}

一般的项目中注解的应用场景可能是记录系统日志,登录验证等地方,结合spring 的aop 去实现

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Logined
{
    /**
     * 其它附加信息
     * @return
     */
    String[] others() default {};
}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * @author chengaoxing on 2019/8/1
 */
@Aspect// 这个注解表明 使用spring 的aop,需要开启aop 
@Component
public class LoginedAspect {
    @Around(value = "@annotation(logined)")// 标示遇到logined这个注解的方法进行拦截 @Around
    public Object checkLogin(ProceedingJoinPoint join, Logined logined) throws Throwable {
        // 获取到使用了注解的方法的参数 这里面根据测试类 是 request,response
        Object[] args = join.getArgs();
        //获取使用了注解的方法的 注解的参数  这里面根据测试类 是 username,password
        String[] orherString = logined.others();
        // 执行使用注解方法并返回信息
        Object ret = join.proceed();
        // 下面是验证登录身份的逻辑代码 省略。。。。。。。
        ...
        // 执行原方法
        Object result = join.proceed(); 
        return result;
    }
}

上面的一个切面通过 @Around(value = "@annotation(logined)")拦截logined注解,在切面执行登陆的信息

你可能感兴趣的:(自定义注解)