Spring 50例常见错误(六)

文章整理来源: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 ======= ");
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值