AOP
官方文档Core Technologies (spring.io)
AOP 的术语
-
连接点
Joinpoint
连接点描述的是程序执行的某个特定位置。如一个类的初始化前、初始化后,或者类的某个方法调用前、调用后、方法抛出异常后等等。一个类或一段程序代码拥有一些具有边界性质的特定点,这些特定点就称为连接点。连接点用来定义在目标程序的哪里通过 AOP 加入新的逻辑。
Spring 仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
官方原文
A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
-
切入点
Pointcut
切入点是一个连接点的过滤条件,AOP 通过切入点定位到特定的连接点。每个类都拥有多个连接点:例如 UserService 类中的所有方法实际上都是连接点。换言之,连接点相当于数据库中的记录,切点相当于查询条件。切入点和连接点不是一对一的关系,一个切入点对应多个连接点,切入点通过org.springframework.aop.Pointcut
接口进行描述,它使用类和方法作为连接点的查询条件。官方原文
A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.
-
通知
Advice
切面在某个具体的连接点采取的行为或行动,称为通知。切面的核心逻辑代码都写在通知中,有人也称之为增强或者横切关注点。通知是切面功能的具体实现,通常是业务代码以外的需求,如日志、验证等。
常用的接口
-
前置通知:org.springframework.aop.MethodBeforeAdvice
-
后置通知:org.springframework.aop.AfterReturningAdvice
-
异常通知:org.springframework.aop.ThrowsAdvice 该接口没有要实现的方法,需要自定义一个 afterThrowing()方法。
-
环绕通知:org.aopalliance.intercept.MethodInterceptor
-
官方原文 A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.
-
切面
Aspect
切面是通知(Advice)和切点(Pointcut)的结合,通知和切点共同定义了切面的全部内容。因为通知定义的是切面的"要做什么"和"在何时做",而切点定义的是切面的"在何地做"。将两者结合在一起,就可以完美的。最后实现切面在何时,何地,做什么。
官方原文
A modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented by using regular classes (the schema-based approach) or regular classes annotated with the annotation (the @AspectJ style).@Aspect
切入点指示符
切入点指示符用来指示切入点表达式目的,在 Spring AOP 中目前只有执行方法这一个连接点,Spring AOP 支持的 AspectJ 切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内 的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
AspectJ 切入点支持的切入点指示符还有: call
、get
、set
、preinitialization
、staticinitialization
、initialization
、handler
、adviceexecution
、withincode
、cflow
、cflowbelow
、if
、@this
、@withincode
;但 Spring AOP 目前不支持这些指示符,使用这些指示符将抛出 IllegalArgumentException 异常。这些指示符 Spring AOP 可能会在以后进行扩展。
切入点使用示例
- execution:使用“execution(方法表达式)”匹配方法执行;
模式 | 描述 |
---|---|
public * *(..) | 任何公共方法的执行 |
_ com.mqb.IPointcutService._() | com.mqb 包及所有子包下 IPointcutService 接口中的任何 无参方法 |
_ com.mqb.._() | com.mqb 包及所有子包下任何无参方法 |
_ com.mqb.._(..) | com.mqb 包及所有子包下任何方法 |
_ cn.javass..IPointcutService.*(_) | cn.javass 包及所有子包下 IPointcutService 接口的任何只有一个参数的方法 |
_ (!cn.javass..IPointcutService+)._(..) | 非“cn.javass 包及所有子包下 IPointcutService 接口及子类型”的任何方法 |
_ cn.javass..IPointcutService+._() | cn.javass 包及所有子包下 IPointcutService 接口及子类型的的任何无参方法 |
* cn.javass..IPointcut*.test*(java.util.Date) | cn.javass 包及所有子包下 IPointcut 前缀类型的的以 test 开头的只有一个参数类型为 java.util.Date 的方法,注意该匹配是根据方法签名的参数类型进行匹配的,而不是根据执行时传入的参数类型决定的。如定义方法:public void test(Object obj);即使执行时传入 java.util.Date,也不会匹配的 |
_ cn.javass..IPointcut*.test_(..) throws IllegalArgumentException, ArrayIndexOutOfBoundsException | cn.javass 包及所有子包下 IPointcut 前缀类型和方法名以 test 为前缀的方法,且抛出 IllegalArgumentException 和 ArrayIndexOutOfBoundsException 异常 |
_ (cn.javass..IPointcutService+&& java.io.Serializable+)._(..) | 任何实现了 cn.javass 包及所有子包下 IPointcutService 接口和 java.io.Serializable 接口的类型的任何方法 |
@java.lang.Deprecated * *(..) | 任何持有@java.lang.Deprecated 注解的方法 |
(@cn.javass..Secure _) _(..) | 任何返回值类型持有@cn.javass..Secure 的方法 |
-
within 使用“within(类型表达式)”匹配指定类型内的方法执行;
模式 描述 within(cn.javass.._) cn.javass 包及子包下的任何方法执行 within(cn.javass..IPointcutService+) cn.javass 包或所有子包下 IPointcutService 类型及子类型的任何方法 within(@cn.javass..Secure _) 持有 cn.javass..Secure 注解的任何类型的任何方法必须是在目标对象上声明这个注解,在接口上声明的对它不起作用 -
this:使用“this(类型全限定名)”匹配当前 AOP 代理对象类型的执行方法;注意是 AOP 代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;注意 this 中使用的表达式必须是类型全限定名,不支持通配符;
模式 | 描述 |
---|---|
this(cn.javass.spring.chapter6.service.IPointcutService) | 当前 AOP 对象实现了 IPointcutService 接口的任何方法 |
this(cn.javass.spring.chapter6.service.IIntroductionService) | 当前 AOP 对象实现了 IIntroductionService 接口的任何方法也可能是引入接口 |
- target :使用“target(类型全限定名)”匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意 target 中使用的表达式必须是类型全限定名,不支持通配符;
模式 描述 target(cn.javass.spring.chapter6.service.IPointcutService) 当前目标对象(非 AOP 对象)实现了 IPointcutService 接口的任何方法 target(cn.javass.spring.chapter6.service.IIntroductionService) 当前目标对象(非 AOP 对象) 实现了 IIntroductionService 接口的任何方法不可能是引入接口 - args:使用“args(参数类型列表)”匹配当前执行的方法传入的参数为指定类型的执行方法;注意是 匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,通配符不支持;args 属于动态切入点,这种切入点开销非常大,非特殊情况最好不要使用;
模式 | 描述 |
---|---|
args (java.io.Serializable,..) | 任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args 指定的参数类型是在运行时动态匹配的 |
通知(advice)的类型
注解 | 含义 |
---|---|
@Before | 当切点在连接点开始执行前触发,参数可以写切入点的方法名,或者是 exexute(方法表达式) |
@After | 当切点在连接点执行完成后触发,通常可以进行资源释放等等 |
@AfterReturning | 当方法执行返回后触发 |
@AfterThrowing | 当抛出异常时执行 |
@Around | 包围一个连接点的通知,类似 Web 中 Servlet 规范中的 Filter 的 doFilter 方法。可以在方法的调用前后完成自定义的行为, 也可以选择不执行。 |