Spring总结2:面向切面编程 AOP
散布于应用中的多处的 功能被称为横切关注点。
通过依赖注入(DI),可以对应用中的对象之间进行解耦;通过AOP,可以将关注点与它们所影响的对象之间进行解耦。比如场景:事务、安全、缓存等。
在面向切面编程时,我们可以在集中一个地方定义通用功能,可以通过声明的方式定义这个功能要以何种方式在何处应用,而不用修改受影响的类。
Spring只支持方法级别的连接点
AOP术语
描述切面常用的术语有:通知(Advice)、切点(Pointcut)、连接点(Join point)
通知(Advice)
通知定义了切面是什么以及何时使用,及切面的工作被称为通知。
Spring的切面可以应用在下面5中类型的通知:
- 前置通知(Before):在目标方法调用前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知功能,不关心方法的输出内容;
- 返回通知(After-returning):在目标方法成功执行之后调用通知功能;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知功能;
- 环绕通知(Around):包裹目标方法,在被通知的方法调用之前和调用之后执行自定义的方法。
连接点(Join point)
连接点是在应用执行过程中能够插入切面的一个点。比如:调用方法时、抛出异常时,甚至是修改一个字段时。Spring只支持方法级别的连接点
切点(Pointcut)
如果说通知(Advice)定义了切面是“什么”和“何时”的话,那么切点(Pointcut)就定义了“何处”。切点有助于缩小切面所通知的连接点的范围,切点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名或利用正则表达式定义所匹配的类或方法名称来指定切点。切点定义了哪些连接点会得到通知。
切面(Aspect)
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容:是什么,在何时及何处完成其功能。
引入(Introduction)
引入允许我们向现有的类添加新方法或属性。
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定连接点被织入到目标对象中。在目标对象的生命周期有多个点可以进行织入:
- 编译期:切面在目标类编译期时被织入,该方式需要特殊的编译器。AspectJ的织入编译器就是在编译期织入切面的。
- 类加载期:切面在目标类加载到JVM时被织入,该方式需要特殊的类加载器。AspecJ5的加载时织入支持类加载期织入切面。
- 运行期:切面在应用运行的某个时刻被织入。在织入切面时,AOP容器会为目标对象动态创建代理类。Spring AOP是以这种方式织入切面的。
Spring中可以使用AspectJ的切点表达式语言定义切面:
AspectJ指示器 | 描述 |
---|---|
args | 限制连接点匹配参数为指定类型的执行方法。limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types |
@args | 限制连接点匹配参数由指定注解标注的执行方法。limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s) |
execution | 用于匹配连接点的执行方法。for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP |
this | 限制连接点匹配AOP代理的bean引用为指定类型的类。limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type |
target | 限制连接点匹配目标对象为指定类型的类。limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type |
@target | 限定连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解。limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type |
within | 限制连接点匹配指定的类型。limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP) |
@within | 限制连接点匹配指定注解所标注的类型。(当使用Spring AOP时,方法定义在由指定的注解所标注的类里)limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP) |
@annotation | 限定匹配带有指定注解的连接点limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation |
定义切面
Spring使用AspectJ注解来声明通知方法:
注解 | 通知描述 |
---|---|
@Before | 通知方法会在目标方法调用前执行。 |
@AfterReturning | 通知方法会在目标方法返回后执行。 |
@AfterThrowing | 通知方法会在目标方法排除异常后执行。 |
@After | 通知方法会在目标方法调用后执行,类似finally 。 |
@Around | 通知方法会将目标方法封装包括起来。 |
举例如下:
@Aspect
public class AdviceExample {
@Before("execution(* com.xyz.myapp.Demo.dao.*.*(..))")
public void doBefore() {
// ...
}
@AfterReturning("com.xyz.myapp.Demo.dataAccessOperation()")
public void doAfterReturning() {
// ...
}
@AfterThrowing("com.xyz.myapp.Demo.dataAccessOperation()")
public void doAfterThrowing() {
// ...
}
@After("com.xyz.myapp.Demo.dataAccessOperation()")
public void doAfter() {
// ...
}
@Around("com.xyz.myapp.Demo.businessService()")
public void doAround(ProceedingJoinPoint pjp) {
// ... doBefore
pjp.proceed();
// ... doAfter
}
}
启用AspectJ
JavaConfig中使用@EnableAspectJAutoProxy
启用AspectJ代理。
@Configuration
@EnableAspectJAutoProxy
public class AopConfig() {
}