引言

  1. 什么是注解?有哪些必要知识?
    1. 内置注解?
    2. 元注解?
  2. 注解的使用场景
  3. 注意事项

java.lang.annotation.Annotation中有说明:这是所有注解类型必须继承的公共接口。

以java中提供的@Override注解为例,一个由Override标识的方法声明意为着它将重写父类中中的方法声明。

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

等价于

1
public interface Override extends Annotation{}

元注解

什么是元注解?『它是用于修饰注解的注解,通常用在注解的定义上。例如上面Override注解上的@Target@Retention
java 5中定义了如下四种元注解:

  1. @Target: 定义了注解的作用目标
  2. @Retention:定义注解的生命周期
  3. @Documented: 注解是否应当被保留在JavaDoc中
  4. @Inherited:是否允许子类继承该注解

@Target

@Target用以标识该注解所能出现的位置,具体枚举值定义在java.lang.annotation.ElementType中:

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum ElementType {
TYPE, // 允许被修饰的注解被作用在类、接口、或者枚举声明 上
FIELD, // 允许作用在属性字段上
METHOD, // 允许作用在方法上
PARAMETER, // 允许作用在方法参数上
CONSTRUCTOR, // 允许作用在构造器上
LOCAL_VARIABLE, // 允许作用在本地局部变量上
ANNOTATION_TYPE, // 允许作用在注解上
PACKAGE, // 允许作用在包上
TYPE_PARAMETER, // 类型参数
TYPE_USE,
MODULE
}

@Retention

用以描述注解的生命周期,有如下三种取值:

  1. RetentionPolicy.SOURCE: 仅在编译器可见,不写入class源文件
  2. RetentionPolicy.CLASS:编译器编译为class源文件时存在,在类加载阶段丢弃
  3. RetentionPolicy.RUNTIME: 运行时仍存在

@Documented

注解是否应当被保留在JavaDoc中

@Inherited

@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。
@Inherited注解只对那些@Target被定义为 ElementType.TYPE 的自定义注解起作用。

如何自定义注解

自定义注解有两种实现:

  • 基于拦截器
  • 基于 aop
    下文中,在介绍完如何定义注解后,将分别以拦截器模式和aop方式实现登录校验。

自定义注解

如下指定了一个自定义注解LoginRequired,该注解作用于方法。被编译器保存在class文件中,而且在运行时会被JVM保留,可以通过反射读取;

1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginRequired {
}

实现自定义注解有两种方式:

  • 基于AOP切面
  • 基于拦截器

基于拦截器实现自定义注解

配置 aop 切面

  • @Aspect:声明该类为一个注解类;
  • @Pointcut:定义一个切点,后面跟随一个表达式,表达式可以定义为切某个注解,也可以切某个 package下的方法;

在配置 aop切面前需要先描述相关术语:

Spring AOP 相关术语
  1. 切面Aspect:在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现
  2. 连接点Joinpoint:在Spring AOP中一个连接点代表一个方法的执行
  3. 通知Advice:在切面的某个特定的连接点Joinpoint上执行的动作。通知有各种类型,其中包括aroundbeforeafter等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链
  4. 切入点Pointcut:定义出一个或一组方法,当执行这些方法时可产生通知,Spring缺省使用AspectJ切入点语法。
通知类型
  • 前置通知@Before:在某连接点join point之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)
  • 返回后通知@AfterReturning:在某连接点join point正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回
  • 抛出异常后通知@AfterThrowing:方法抛出异常退出时执行的通知
  • 后通知@After:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
  • 环绕通知@Around:包围一个连接点join point的通知,如方法调用。这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行

执行顺序如下:
aop执行顺序

Spring AOP 配置的两种风格
  • XML风格 = 采用声明形式实现Spring AOP
  • AspectJ风格 = 采用注解形式实现Spring AOP

配置aop

使用场景

自定义注解+拦截器 实现登录校验

自定义注解+AOP 实现日志打印

引用

1.