声明通知Adivce
Advice 与切入点表达式相关联,并在切入点匹配的方法执行之前、之后或周围运行。切入点表达式可以是对命名切入点的简单引用,也可以是就地声明的切入点表达式。
Before Adivce
您可以使用@Before注释在方面中声明Before Advice:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class BeforeExample {@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")public void doAccessCheck() {// ...}
}
如果我们使用inline切入点表达式,我们可以将前面的示例重写为以下示例:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class BeforeExample {@Before("execution(* com.xyz.myapp.dao.*.*(..))")public void doAccessCheck() {// ...}
}
After Returning Advice
返回通知后,当匹配的方法执行正常返回时运行。您可以使用@AfterReturning
注解来声明它:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;@Aspect
public class AfterReturningExample {@AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")public void doAccessCheck() {// ...}
}
你可以有多个通知声明(以及其他成员),都在同一个方面。我们在这些示例中只展示了一个通知声明,以集中每个通知的效果。 |
有时,您需要在通知正文中访问返回的实际值。您可以使用@AfterReturning
绑定返回值的形式来获取该访问权限,如以下示例所示:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;@Aspect
public class AfterReturningExample {@AfterReturning(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",returning="retVal")public void doAccessCheck(Object retVal) {// ...}
}
returning
属性中使用的名称必须与通知方法中的参数名称相对应。- 当目标方法执行返回时,返回值作为相应的参数值传递给该通知方法。
- returning子句还将匹配并限制那些返回值为指定类型方法执行(在这种情况下,
Object
匹配任何返回值)。
请注意,在使用 AfterReturning Advice时,不能返回完全不同类型的引用。
After Throwing Advice
当匹配的方法执行抛出异常退出时,After Throwing Advice执行。您可以使用@AfterThrowing
注解来声明它,如以下示例所示:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;@Aspect
public class AfterThrowingExample {@AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")public void doRecoveryActions() {// ...}
}
通常,您希望建议仅在引发指定类型的异常时运行,并且您还经常需要访问Advice中引发的异常。您可以使用该throwing
属性来限制匹配(如果需要 -Throwable
用作异常类型)并将抛出的异常绑定到通知参数。以下示例显示了如何执行此操作:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;@Aspect
public class AfterThrowingExample {@AfterThrowing(pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",throwing="ex")public void doRecoveryActions(DataAccessException ex) {// ...}
}
throwing属性中使用的名称必须与advice方法中参数的名称相对应。当通过引发异常退出方法执行时,异常作为相应的参数值传递给通知方法。throwing子句还限制只匹配那些引发指定类型的异常(在本例中是DataAccessException)的方法执行 |
After (Finally) Advice
当匹配的方法执行退出时(最终)通知运行。它是通过使用@After
注解来声明的。After通知在方法执行正常和异常返回情况下都能够正常处理。它通常用于释放资源和类似目的。下面的例子展示了如何使用 after advice:
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;@Aspect
public class AfterFinallyExample {@After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")public void doReleaseLock() {// ...}
}
请注意, AspectJ 中的通知被定义为“在 finally 通知之后”,类似于 try-catch 语句中的 finally 块。 它将在连接点(用户声明的目标方法)抛出的任何结果、正常返回或异常时调用,与之相反,
它仅适用于成功的正常返回。 |
Around Advice
最后一种Advice是Around Advice。Around Advice“围绕”匹配方法的执行。它在方法运行之前和之后进行工作,并确定该方法何时、如何以及是否真正开始运行。如果您需要以线程安全的方式在方法执行之前和之后共享状态(例如,启动和停止计时器),则通常使用环绕通知。
始终使用满足您要求的最不强大的建议形式。例如,如果之前的建议足以满足您的需求,请不要使用环绕建议。 |
环绕通知是通过使用@Around注解对方法进行注解来声明的。该方法应声明Object
为其返回类型,并且该方法的第一个参数必须是ProceedingJoinPoint
类型。在通知方法的主体中,您必须调用ProceedingJoinPoint
对象的proceed()方法使目标方法运行。不带参数调用proceed()
将导致调用者的原始参数在调用时提供给底层方法。对于高级用例,该proceed()
方法有一个重载变体,它接受参数数组 ( Object[]
)。调用时,数组中的值将用作底层方法的参数。
当用Object[] 调用时,proceed的行为与由aspectj编译器编译的around advice的proceed的行为稍有不同。对于使用传统aspectj语言编写的around advice,传递给proceed的参数数量必须与传递给around advice的参数数量(而不是基础连接点接受的参数数量) 匹配,并且传递给给定参数位置的值在连接点为值所绑定到的实体添加原始值(如果现在没有意义,请不要担心) 。 Spring采用的方法更简单,更符合其基于代理的、只执行的语义。只有在编译为Spring编写的@Aspectj方面并使用aspectj编译器和weaver的参数时,才需要注意这一区别。有一种方法可以在SpringAOP和AspectJ中编写100%兼容的方面,这将在下面关于建议参数的小节中讨论。 |
around 通知返回的值是方法调用者看到的返回值。例如,一个简单的缓存方面可以从缓存中返回一个值,如果它有一个值,或者调用proceed()
(并返回该值)如果它没有。请注意,proceed
在 around 建议的主体内可能会调用一次、多次或根本不调用。所有这些都是合法的。
如果您将around建议方法的返回类型声明为void,那么null将始终返回给调用者,从而有效地忽略任何processed()调用的结果。因此,建议around建议方法声明Object的返回类型。 advice方法通常应该返回从processed()的调用返回的值,即使基础方法的返回类型为void。但是,根据使用情况,建议可以选择性地返回缓存值、包装值或其他值。 |
下面的例子展示了如何使用 around 通知:
java
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.CommonPointcuts.businessService()")public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {// start stopwatchObject retVal = pjp.proceed();// stop stopwatchreturn retVal;}
}