问题描述
作为一个刚刚开始学习spring boot的小白,最近在研读杨开振老师的《深入浅出SpringBoot2.x》。而在接触到AOP编程时却发现,实践的代码输出与书上所述不同。
问题分析
@Aspect
@Slf4j
public class MyAspect {
@Pointcut("execution(* XXX.UserServiceImpl.printUser(..))")
public void pointCut(){
}
@Before("pointCut()")
public void before() {
log.info("before......");
}
@Around("pointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("around before.......");
joinPoint.proceed();
log.info("around after......");
}
@After("pointCut()")
public void after(){
log.info("after......");
}
@AfterReturning("pointCut()")
public void afterReturn() {
log.info("AfterReturn......");
}
@AfterThrowing("pointCut()")
public void afterThrow() {
log.info("AfterThrow......");
}
}
在测试上述代码时,根据书上的描述,杨老师本人实测的结果应当为
around before......
before......
around after......
after......
afterReturning.......
也就是说先执行的先后顺序为环绕通知,前置通知,后置通知,事后返回通知。而当我以springboot 2.3.2
spring5.2.8
的环境下进行测试时所得结果如下
可以看到与书上的结果差异明显,后置通知的执行实在事后通知之前。考虑到spring boot 版本的不同,于是我便去查询spring 文档,果然发现更新过后AOP的执行顺序已经改变。更改后的描述是这样的。
Before Advice
You can declare before advice in an aspect by using the annotation:@Before
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() {
// ...
}
}
If we use an in-place pointcut expression, we could rewrite the preceding example as the following example:
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
After returning advice runs when a matched method execution returns normally. You can declare it by using the annotation:@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() {
// ...
}
}
| | You can have multiple advice declarations (and other members as well), all inside the same aspect. We show only a single advice declaration in these examples to focus the effect of each one. |
Sometimes, you need access in the advice body to the actual value that was returned. You can use the form of that binds the return value to get that access, as the following example shows:@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) {
// ...
}
}
The name used in the attribute must correspond to the name of a parameter in the advice method. When a method execution returns, the return value is passed to the advice method as the corresponding argument value. A clause also restricts matching to only those method executions that return a value of the specified type (in this case, , which matches any return value).returning``returning``Object
Please note that it is not possible to return a totally different reference when using after returning advice.
After Throwing Advice
After throwing advice runs when a matched method execution exits by throwing an exception. You can declare it by using the annotation, as the following example shows:@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() {
// ...
}
}
Often, you want the advice to run only when exceptions of a given type are thrown, and you also often need access to the thrown exception in the advice body. You can use the attribute to both restrict matching (if desired — use as the exception type otherwise) and bind the thrown exception to an advice parameter. The following example shows how to do so: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) {
// ...
}
}
The name used in the attribute must correspond to the name of a parameter in the advice method. When a method execution exits by throwing an exception, the exception is passed to the advice method as the corresponding argument value. A clause also restricts matching to only those method executions that throw an exception of the specified type ( , in this case).throwing``throwing``DataAccessException
After (Finally) Advice
After (finally) advice runs when a matched method execution exits. It is declared by using the annotation. After advice must be prepared to handle both normal and exception return conditions. It is typically used for releasing resources and similar purposes. The following example shows how to use 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.CommonPointcuts.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}
Around Advice
The last kind of advice is around advice. Around advice runs “around” a matched method’s execution. It has the opportunity to do work both before and after the method runs and to determine when, how, and even if the method actually gets to run at all. Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer, for example). Always use the least powerful form of advice that meets your requirements (that is, do not use around advice if before advice would do).
Around advice is declared by using the annotation. The first parameter of the advice method must be of type . Within the body of the advice, calling on the causes the underlying method to run. The method can also pass in an . The values in the array are used as the arguments to the method execution when it proceeds.@Around``ProceedingJoinPoint``proceed()``ProceedingJoinPoint``proceed``Object[]
| | The behavior of when called with an is a little different than the behavior of for around advice compiled by the AspectJ compiler. For around advice written using the traditional AspectJ language, the number of arguments passed to must match the number of arguments passed to the around advice (not the number of arguments taken by the underlying join point), and the value passed to proceed in a given argument position supplants the original value at the join point for the entity the value was bound to (do not worry if this does not make sense right now). The approach taken by Spring is simpler and a better match to its proxy-based, execution-only semantics. You only need to be aware of this difference if you compile @AspectJ aspects written for Spring and use with arguments with the AspectJ compiler and weaver. There is a way to write such aspects that is 100% compatible across both Spring AOP and AspectJ, and this is discussed in the following section on advice parameters. proceed``Object[]``proceed``proceed``proceed
|
The following example shows how to use around advice:
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 stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
The value returned by the around advice is the return value seen by the caller of the method. For example, a simple caching aspect could return a value from a cache if it has one and invoke if it does not. Note that may be invoked once, many times, or not at all within the body of the around advice. All of these are legal.proceed()``proceed
结论
大概意思就是@After
现在是直到切点方法退出之后才会执行,而@AfterReturn
和@AfterThrow
则是在抛出异常或者得到返回结果时就会执行
本人纯小白,如有任何问题还请大家指出