多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[toc] 建议与切入点表达式相关联,并在切入点匹配的方法执行之前,之后或周围运行。 切入点表达式可以是对命名切入点的简单引用,也可以是在适当位置声明的切入点表达式。 ## Before advice 建议在方法开始之前执行,在切面中使用`@Before`注解 ~~~java 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() { // ... } } ~~~ 如果要在此处使用切点表达式,可以重写 ~~~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.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... } } ~~~ 有时候你需要在建议方法体内使用切点的返回值,可以如下 ~~~java 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的属性值必须和方法参数名称一致.参数类型也要和切点返回类型一致,Object符合所有类型. ## 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.SystemArchitecture.dataAccessOperation()") public void doRecoveryActions() { // ... } } ~~~ 通常,需要在建议方法体内处理异常,可以使用属性`throwing `匹配方法参数 ~~~java 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) { // ... } } ~~~ ## After (finally) advice 建议在方法结束之后执行,无论正常还是异常结束.使用注解`@After`,需要考虑正常和异常的返回情况 ~~~java 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() { // ... } } ~~~ ## Around advice 最后一个建议是围绕方法执行,.经常用在线程安全模式下共享数据状态(如开始和结束的计时器).使用满足需求的建议即可,不要扩大范围,如before能满足需求,就不要用around. 范围建议,使用注解`@Around`.建议方法的第一个参数必须是`ProceedingJoinPoint`.在建议方法体内执行`ProceedingJoinPoint`的`proceed()`方法会执行切点方法.`proceed()`方法也可能要求传入`Object[]`作为参数.在`proceed()`方法前后可以写需要的业务逻辑 ~~~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.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; } } ~~~ ## Advice parameters spring提供了所有的建议类型--意味着你要在方法签名上声明参数而不是使用`Object[]`. ### Access to the current JoinPoint 所有建议方法的第一个参数都是`org.aspectj.lang.JoinPoint`(围绕建议的参数是`ProceedingJoinPoint`是`JoinPoint`的子类).`JoinPoint`提供了很多实用的方法,如`getArgs()`返回方法的参数,`getThis()`返回代理对象,`getTarget()`返回目标对象,`getSignature()`返回方法声明,`toString()`打印被建议方法的描述. ### Passing parameters to advice 在`after returning`和`after throwing`处已经知道如何绑定返回结果.为了在建议方法体内使用返回结果,可以使用绑定模式`args` 举例说明,假设你建议的表达式为dao层的方法,且第一个参数为`Account`对象,在建议只是方法体内要访问这个对象,如下写法 ~~~java @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") public void validateAccount(Account account) { // ... } ~~~ 切点表达式的`args(account,..)`部分实现了两个目标:第一,限定表达式的方法至少有一个参数,且这个参数的类型为`Account`;第二,能够在建议方法体内通过参数`account `来访问对象. 另一种写法,通过声明切点时提供`Account `作为参数,然后在建议时引用这个声明的切点,如下: ~~~java @Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") private void accountDataAccessOperation(Account account) {} @Before("accountDataAccessOperation(account)") public void validateAccount(Account account) { // ... } ~~~ ### Advice parameters and generics ### Determining argument names ### Proceeding with arguments ## Advice ordering 如果有多个建议在同一个接入点执行,会发生什么呢?和AspectJ保持一致, 在接入点之前执行的建议,优先级高的先执行,在接入点之后执行的建议,优先级高的后执行. 控制优先级需要实现`org.springframework.core.Ordered`类,或者使用注解`Order `,底层通过`Ordered.getValue()`来确定优先级. 如果两个建议在同一个接入点执行,没有定义优先级,考虑把建议合并到一个建议中,或者重构,每个建议在独立的切面类中,实现排序.