Spring 中的AOP编程是与JAVA中OOP同等的,在OOP中模块的基本单位是类,而在AOP中模块的基本单位是切面,其实AOP的底层还是通过代理来实现的。
一、基本术语
1、Aspect
将横切多个业务对象的程序独立出来模块化,该模块可以无侵入式地集成到业务对象中。
如,事务、日志、权限
2、Advice
是指切面的具体实现,如记录日志、验证权限
通知有各种类型,其中包括”before”、”around”、”throw”等通知。
3、Joinpoint
通知执行的时机,如方法调用时或抛出异常时
4、Pointcut
切入点是感兴趣的连接点(即并不是每个方法调用时都会反执行)。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(如执行某个特定名称的方法时)
切入点表达式如何和连接点匹配是AOP的核心
5、Target
被一个或者多个切面所通知的对象
是一个被代理对象
6、AOP Proxy
AOP框架创建的对象,用来实现切面的功能
在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理
7、Weaving
把切面连接到其它 的应用程序类型或者对象上,并创建一个被通知对象,是在运行时完成织入。
二 、声明一个Pointcut
Pointcut声明中可以用的标识符有execution、within、this、target、args、@target、@args、@within、@annotation。
下面是一个简单的Pointcut声明
@Pointcut("execution(* transfer(..))")// the pointcut 表达式
private void anyOldTransfer() {}// the pointcut 方法签名
要注意的是方法签名只能是返回空值。
该Pointcut匹配的是方法名为transfer,返回值和参数个数任意的方法执行。
其中Pointcut表达式遵从如下模式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
modifiers-pattern:代表匹配的方法的访问控制权限(public 、protected、private可选项)
ret-type-pattern:代表匹配方法的返回值类型(必选项)
declaring-type-pattern:声明类型(可选项),如果指定了,就应该包括一个.接尾,把它添加到相关的组件中。
name-pattern:匹配的方法名。
param-pattern:匹配的参数,如果是..则表示有0或多个参数,如果没有则表示无参数。
下面是常见的Pointcut表达式的例子:
1、匹配任意公有方法的执行
execution(public * *(..))
2、任意以set开头的方法的执行
execution(* set*(..))
3、AccountService接口中方法的执行
execution(* com.xyz.service.AccountService.*(..))
4、service包下方法的执行
execution(* com.xyz.service.*.*(..))
5、service包及子包中方法的执行
execution(* com.xyz.service..*.*(..))
6、任何位于service包下的连接点
within(com.xyz.service.*)
7、任何位于service包及其子包下的连接点
within(com.xyz.service..*)
注:Pointcut表达式中也可以有&&,| |,!这些逻辑表达式,如:
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
上例中名为tradingOperation
的连接点引用了名字为anyPublicOperation
和inTrading
的连接点,当上面两个条件同时满足时才会匹配tradingOperation
连接点
三、 声明一个advice
1、Before advice
该通知可用@Before
注解
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
这个表示dataAccessOperation方法调用之前doAccessCheck方法会被调用
2、After returning advice
该通知在方法正常返回时被调用
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
当然如果你想接收相关的返回值还可以这样配置
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}
注:returning="retVal"
中的retVal值必须和doAccessCheck方法参数名一致,当dataAccessOperation()返回时,它的返回值会被retVal
接收从而传给doAccessCheck()
函数,这里引用的dataAccessOperation()是一个接入点
2、After throwing advice
这个通知是在方法产生异常时执行
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
}
和获取返回值一样,在doRecoveryActions
方法中我们也可以通过一个参数接收一个异常
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
}
4、After (finally) advice
这个通知是在方法无论以怎样的方式退出时,都会执行。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}
5、Around advice
该通知可运行在方法执行之前和之后,也就是同时具有Before
和After
的功能,不过Before
和After
能完成的需求,就不要用Around advice
了。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
该通知方法中还要接收一个ProceedingJoinPoint
的参数,这样执行目标方法,目标方法是通过pjp.proceed();
完成调用的