文章整理来源:Spring编程常见错误50例_spring_spring编程_bean_AOP_SpringCloud_SpringWeb_测试_事务_Data-极客时间
案例13:不同增强注解的执行顺序
对于 类 ElectricService 的方法 charge() 使用 @Before 和 @Around 等不同注解去做此方法的增强,但需要明确执行顺序。
@Service
public class ElectricService {
public void charge() throws Exception {
System.out.println("Electric charging ...");
}
}
-----------------------------------------------------
@Aspect
@Service
public class AopConfig {
@Before("execution(* com.spring.puzzle.class6.example1.ElectricService.charge()) ")
public void checkAuthority(JoinPoint pjp) throws Throwable {
System.out.println("validating user authority");
Thread.sleep(1000);
}
@Around("execution(* com.spring.puzzle.class6.example1.ElectricService.charge()) ")
public void recordPerformance(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("charge method time cost: " + (end - start));
}
}
例子预期是先执行 @Before ,再执行 @Around ,但实际却相反
解析:在创建代理对象的过程中,最终 Advisors 的顺序是由两点决定,1.candidateAdvisors 的顺序;2. sortAdvisors 进行的排序。
同一个切面中,不同类型的增强方法被调用的顺序依次为 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class。
解决: 步骤 1. 将 ElectricService.charge() 的业务逻辑全部移动到 doCharge(),在 charge() 中调用 doCharge();2.性能统计只需要拦截 doCharge();3.权限统计增强保持不变,依然拦截 charge()。
@Service
public class ElectricService {
@Autowired
ElectricService electricService;
public void charge() {
electricService.doCharge();
}
public void doCharge() {
System.out.println("Electric charging ...");
}
}
------------------------------------------------
@Aspect
@Service
public class AopConfig {
@Before("execution(* com.spring.puzzle.class6.example1.ElectricService.charge()) ")
public void checkAuthority(JoinPoint pjp) throws Throwable {
System.out.println("validating user authority");
Thread.sleep(1000);
}
@Around("execution(* com.spring.puzzle.class6.example1.ElectricService.doCharge()) ")
public void recordPerformance(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("charge method time cost: " + (end - start));
}
}
案例14:同种增强注解的执行顺序
同是使用了 @Before 注解,想要 抛出校验错误在先 ,但却让 logBeforeMethod() 方法执行在先
@Service
public class ElectricService {
public void charge() {
System.out.println("Electric charging ...");
}
}
----------------------------------------------------------
@Aspect
@Service
public class AopConfig {
@Before("execution(* com.spring.puzzle.class5.example2.ElectricService.charge())")
public void logBeforeMethod(JoinPoint pjp) throws Throwable {
System.out.println("step into ->"+pjp.getSignature());
}
@Before("execution(* com.spring.puzzle.class5.example2.ElectricService.charge()) ")
public void validateAuthority(JoinPoint pjp) throws Throwable {
throw new RuntimeException("authority check failed");
}
}
解析:在 METHOD_COMPARATOR 的静态代码块中,AOP 注解比较器的连接顺序,先按案例12 中的注解顺序排序,同种注解的按照方法名进行排序
static {
//第一个比较器,用来按照增强类型排序
Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
new InstanceComparator<>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
(Converter<Method, Annotation>) method -> {
AspectJAnnotation<?> annotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return (annotation != null ? annotation.getAnnotation() : null);
})
//第二个比较器,用来按照方法名排序
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}
解决:修改 logBeforeMethod() 方法名,使其排在 validateAuthority() 方法名后面 或者 修改 validateAuthority() 方法名,使其排在 logBeforeMethod() 方法名前面
@Aspect
@Service
public class AopConfig {
@Before("execution(* com.spring.puzzle.class6.example2.ElectricService.charge())")
public void logBeforeMethod(JoinPoint pjp) throws Throwable {
System.out.println("step into ->"+pjp.getSignature());
}
@Before("execution(* com.spring.puzzle.class6.example2.ElectricService.charge()) ")
public void checkAuthority(JoinPoint pjp) throws Throwable {
throw new RuntimeException("authority check failed");
}
}
疑问:
若是在不同类中使用同种 AOP 注解 或 不同种 AOP 注解 ,顺序又是怎么定的?
结果:对于不同类,先按类名排序;同一类下的,先按注解类的顺序排序,再按方法名排序
AOPConfig1 中的 @Before 要先于 AOPConfig2 中的 @Around 先被拦截调用
==== aopConfig1 around method =======
==== aopConfig1 before method ======= (先于 AOPConfig2 中的 @Around )
==== aopConfig2 around method =======
@Aspect
@Service
public class AOPConfig1 {
@Before(value = "execution(* org.scut.lmall.service.LightService.pay())")
public void beforeGetName() throws Throwable {
System.out.println("==== aopConfig1 before method ======= ");
}
@Around(value = "execution(* org.scut.lmall.service.LightService.pay())")
public void aroundGetName(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("==== aopConfig1 around method ======= ");
}
}
--------------------------------------------------------------------------
@Aspect
@Service
public class AOPConfig2 {
@Around(value = "execution(* org.scut.lmall.service.LightService.pay())")
public void aroundGetName(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("==== aopConfig2 around method ======= ");
}
}