public interface UserService {
void doProcess(long userId);
}
@Service
public class DefaultUserService implements UserService {
@Override
public void doProcess(long userId) {
if (userId <= 0) {
throw new IllegalArgumentException("userId <= 0");
}
}
}
@Aspect
@Component
public class UserServiceLogAspect {
@Pointcut("execution(public * com.example.springaop.service.impl.DefaultUserService.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("++++++++++> @Before " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
}
@After("pointcut()")
public void after(JoinPoint joinPoint) {
System.out.println("<++++++++++ @After " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning("pointcut()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("<++++++++++ @AfterReturning " + joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs()));
}
@AfterThrowing(value = "pointcut()", throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
System.err.println("<++++++++++ @AfterThrowing "
+ joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs())
+ " errMsg=[" + throwable.getMessage() + "]");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
final String sig = joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs());
Object result = null;
System.out.println("----------> @Around##Before " + sig);
try {
result = joinPoint.proceed();
} catch (Throwable e) {
System.err.println("<---------- @Around##AfterThrowing " + sig + " errMsg=[" + e.getMessage() + "]");
throw e; // re-throw
}
System.out.println("<---------- @Around##After " + sig);
return result;
}
}
基于以上的切面逻辑,在 UserService#doProcess(long) 方法执行前后以及异常情况下的各个切面的执行顺序是怎样的呢?
执行下面测试代码,观察输出:
@SpringBootTest
class SpringAopApplicationTestsSpring5 {
@Autowired
private UserService userService;
// 测试正常情况
@Test
public void testNormally() {
System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
this.userService.doProcess(1);
}
// 测试异常情况
@Test
public void testError() {
System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
this.userService.doProcess(-1);
}
}
SpringBootVersion: 2.5.7 SpringVersion: 5.3.13
----------> @Around##Before doProcess[1]
++++++++++> @Before doProcess[1]
<++++++++++ @AfterReturning doProcess[1]
<++++++++++ @After doProcess[1]
<---------- @Around##After doProcess[1]
SpringBootVersion: 2.5.7 SpringVersion: 5.3.13
----------> @Around##Before doProcess[-1]
++++++++++> @Before doProcess[-1]
<++++++++++ @AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @Around##AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @After doProcess[-1]
<---------- @Around##After doProcess[-1]
执行下面测试代码,观察输出:
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringAopApplicationTestsSpring4 {
@Autowired
private UserService userService;
@Test
public void testNormally() {
System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
this.userService.doProcess(1);
}
@Test
public void testError() {
System.out.println("SpringBootVersion: " + SpringBootVersion.getVersion() + "tSpringVersion: " + SpringVersion.getVersion() + "n");
this.userService.doProcess(-1);
}
}
SpringBootVersion: 1.5.9.RELEASE SpringVersion: 4.3.13.RELEASE
----------> @Around##Before doProcess[1]
++++++++++> @Before doProcess[1]
<---------- @Around##After doProcess[1]
<++++++++++ @After doProcess[1]
<++++++++++ @AfterReturning doProcess[1]
SpringBootVersion: 1.5.9.RELEASE SpringVersion: 4.3.13.RELEASE
----------> @Around##Before doProcess[-1]
++++++++++> @Before doProcess[-1]
<++++++++++ @After doProcess[-1]
<---------- @Around##AfterThrowing doProcess[-1] errMsg=[userId <= 0]
<++++++++++ @AfterThrowing doProcess[-1] errMsg=[userId <= 0]
在 Spring5 中各个切面的执行顺序如下:
环绕通知 @Around 的逻辑包裹着 @Before、 @AfterReturing、 @AfterThrowing、 @Returing 这些切面。@After 类比于 finally 块的代码一样在最后执行。
在 Spring4 中各个切面的执行顺序如下:
环绕通知 @Around 的逻辑 并不是 包裹着 @Before、 @AfterReturing、 @AfterThrowing、 @Returing 这些切面。@After 在 @AfterReturing 或 @AfterThrowing 之前执行。