AOP(审计日志的开发与延伸)

注解 Annotation

Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。

  Annotation提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value”对中。

  Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据。值得指出的是:Annotation不能影响程序代码的执行,无论增加、删除Annotation,代码都始终如一地执行。如果希望让程序中的Annotation能在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问的处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool)


Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。

三个基本的Annotation:

@Override 限定重写父类的方法

@Deprecated 标示已过时

@SuppressWarnings 抑制编译器警告

自定义Annotation
定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像

定义一个简单的注解:

public @interface GetLog {
}

带成员变量的Annotation,Annotation的成员变量在Annotation定义中以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型

同时使用时也要指定相应的变量值

public @interface GetLog {
    //定义成员变量
    String name();
}

@GetLog(name = "log")
public void getName(){}

我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字;

public @interface GetLog {
    //定义成员变量
    String description() default "${description}";
}

根据我们介绍的Annotation是否可以包含成员变量,我们可以把Annotation分为如下两类:

标记Annotation: 一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如前面介绍的@Override。

元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以也被称为元数据Annotation。

提取Annotation信息
Java使用Annotation接口来代表程序元素前面的注释(反射的时候用它来接收注解对象),该接口是所有Annotation类型的父接口;

Java在java.lang.reflect包下新增了AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类(注意以下是类:

Class:类定义。

Constructor:构造器定义。

Field:类的成员变量定义。

Method:类的方法定义。

Package:类的包定义。

java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement接口是所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:

getAnnotation(Class annotationClass); //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。

Annotation[] getAnnotations(); //返回该程序元素上存在的所有注释。

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。

//@Retention注解指定GetLog注解保留多久
@Retention(RetentionPolicy.RUNTIME)
//@Target注解指定注解能修饰的目标(只能是方法)
@Target(ElementType.METHOD)
public @interface GetLog {
    String description() default "${description}";
}

定义Annotation时使用了@Retention@Target两个系统元注释,其中@Retention注释指定Test注释可以保留多久,@Target注释指定Test注释能修饰的目标().
仅仅使用注释来标识程序元素对程序是不会有任何影响的,这也是Java注释的一条重要原则。
@Retention @Retention只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。 value成员变量的值只能是如下三个: RetentionPolicy.CLASS: 编译器将把注释记录在class文件中。当运行Java程序时,JVM不在保留注释,这是默认值。 RetentionPolicy.RUNTIME: 编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。 RetentionPolicy.SOURCE: 注解仅存在于源码中,在class字节码文件中不包含。 @Target @Target也是用于修饰一个Annotation定义,它用于指定被修饰Annotation能用于修饰那些程序元素。@Target Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个:
ElementType.ANNOTATION_TYPE: 指定该策略的Annotation只能修饰Annotation。 ElementType.CONSTRUCTOR: 指定该策略的Annotation能修饰构造器。 ElementType.FIELD: 指定该策略的Annotation只能修饰成员变量。 ElementType.LOCAL_VARIABLE: 指定该策略的Annotation只能修饰局部变量。 ElementType.METHOD: 指定该策略的Annotation只能修饰方法。 ElementType.PACKAGE: 指定该策略的Annotation只能修饰包定义。 ElementType.PARAMETER: 指定该策略的Annotation可以修饰参数。 ElementType.TYPE: 指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。 @Documented @Documented用于指定该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。
@Inherited @Inherited 元 Annotation指定被它修饰的Annotation将具有继承性:如果某个类使用了A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释. ### AOP ### 动态代理