Spring AOP切点和通知

前言

Spring切面由切点和通知组成。Spring AOP是基于代理的,包括JDK动态代理或者CGLIB动态代理,只支持方法级别的连接点,因此Spring AspectJ风格切点表达式仅仅是AspectJ的子集。表达式之间可以用&&、||、!表示与或非、如果是在XML中配置,那么需要用and、or、not代替&&、||、!。注意,切点的定义如果只到子类,那么父类的方法无法被拦截。


通知

通知表示切面在何时完成什么操作。Spring提供了5个注解来定义通知,注解的值可以是切点表达式,也可以是@Pointcut注解标注的方法,@Pointcut的值是切点表达式。


@Before

通知方法会在目标方法调用之前执行。
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

@Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }

@AfterReturning

通知方法会在目标方法返回后调用。
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
如果想要将方法返回的参数传递给通知方法,可以这么配置
@AfterReturning(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }
returning="retVal"必须和通知方法doAccessCheck里的参数名一样,Object retVal表示可以接收任何类型的返回值。

@AfterThrowing

通知方法会在目标方法抛出异常后调用。如果想要将抛出的异常传递给通知方法,可以这么配置
@AfterThrowing(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }
throwing="ex"必须和通知方法doRecoveryActions里的参数名一样,DataAccessException ex表示可以接收DataAccessException类型的异常。

@After

通知方法会在目标方法返回或抛出异常后调用。

@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }

@Around

环绕通知,通知方法会将目标方法封装起来,这样就可以在目标方法前后做某些处理。这个注解标注的通知方法第一次参数必须是ProceedingJoinPoint类型的,ProceedingJoinPoint的proceed方法表示调用目标方法,此方法仅能调用一次。proceed还有个重载的方法,可以传入一个Object数组,如果调用ProceedingJoinPoint.getArgs()可以得到目标方法参数数组,将这个数组传给proceed,就等于不传参数的proceed。也可以自定义一个数组,传给proceed,但是参数个数和类型需要和目标方法保持一致。
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }

切点

切点表示切面在何处完成通知所定义的内容,Spring中切点只能是方法级别。

execution
匹配方法,这是最为常用的表达式。格式:execution(访问修饰符-可选 返回类型 方法所属类型-可选 方法名(方法参数) 抛出异常)
返回类型可以用*代表任意类型。
方法所属类型是类的完全限定名,可以用*匹配任意字符,除了.,因此*无法匹配子包或者内部类,..表示一串以.开始和结束的字符串,因此可以用来匹配子包和内部类。
方法名可以用*匹配任意字符。
方法参数可以用..表示任意数量任意类型的参数,*可以代表一个任意类型的参数,比如(*,String),表示第一个参数类型任意,第二个参数是String类型。
execution(public * *(..)):任意public方法
execution(* set*(..)):任意方法名以set开头的方法
execution(* com.xyz.service.AccountService.*(..)):com.xyz.service.AccountService类中的任意方法
execution(* com.xyz.service.*.*(..)):com.xyz.service包中的任意类型的任意方法,不包括子包和内部类
execution(* com.xyz.service..*.*(..)):com.xyz.service包中的任意类型的任意方法,包括子包和内部类

within
匹配某些类型中的所有方法。
within(com.xyz.service.*):com.xyz.service包中的任意类型的所有方法,不包括子包和内部类
within(com.xyz.service..*)::com.xyz.service包中的任意类型的所有方法,不包括子包和内部类

@within
匹配某些类型中的所有方法,这些类型具有指定的注解。
@within(org.springframework.transaction.annotation.Transactional):具有@Transactional注解的类中的所有方法。

this 
匹配代理类是某个类型的实例。
this(com.xyz.service.AccountService):代理类是com.xyz.service.AccountService的实例(继承类或者实现接口)

target
匹配某个类型中的所有方法。
target(com.xyz.service.AccountService):com.xyz.service.AccountService中的所有方法。

@target
匹配某个类型中的某些方法,这些方法具有指定类型的注解。
@target(org.springframework.transaction.annotation.Transactional):目标类中具有@Transactional 注解的方法。

args
匹配运行时传递给方法的参数,参数具有指定的数量和类型。
args(java.io.Serializable):表示运行时传递给方法的只有一个参数,且参数是Serializable类型。和execution(* *(java.io.Serializable))不一样,后者表示代码中声明的方法参数类型是Serializable。

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}
上面的例子是args另外一种用法,向通知方法传参。args(account,..)表示至少有一个参数,且参数的类型是Account,参数值会被传递给通知方法中的account。


@args
匹配运行时传递给方法的参数,参数的类型要具有指定类型的注解。
@args(com.xyz.security.Classified):表示运行时传递给方法的只有一个参数,且参数具有@Classified注解。

@annotation 
匹配方法,这个方法要具有指定类型的注解。
@annotation(org.springframework.transaction.annotation.Transactional):目标方法具有@Transactional 注解。

你可能感兴趣的:(Spring)