自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

嚴(yán)重!Spring AOP Bug導(dǎo)致切面重復(fù)執(zhí)行

開發(fā) 前端
在Spring中@Aspect定義的切面最終都會轉(zhuǎn)換為Advisor對象,當(dāng)代理類在執(zhí)行時(shí)會遍歷所有符合添加的Advisor然后從中取出對應(yīng)的Advice(MethodInterceptor)對象。

環(huán)境:Spring6.1.7

1. 問題復(fù)現(xiàn)

為了提高代碼的復(fù)用性和維護(hù)性,我們設(shè)計(jì)了一個(gè)通用的抽象的切面父類。這個(gè)父類封裝了通用的切面邏輯,如日志記錄、性能監(jiān)控、異常處理等。通過繼承這個(gè)抽象父類,具體的業(yè)務(wù)切面可以輕松地?cái)U(kuò)展或定制這些通用功能,而無需從零開始編寫切面邏輯。如下示例:

public abstract class CommonAspect {
  @Pointcut("execution(public * com.pack..*.*(..))")
  private void commonPointcut() {}
  @Before("bean(*Service)")
  protected void beforeLog(JoinPoint jp) {
    System.out.println("通用日志記錄...") ;
  }
  @Around("@annotation(monitor)")
  protected Object monitorRun(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {
    Object ret = null ;
    // TODO
    ret = pjp.proceed() ;
    // TODO
    return ret ;
  }
  // other
}

具體切面子類

@Component
@Aspect
public class LogAspect extends CommonAspect {
  @Override
  public void beforeLog(JoinPoint jp) {
    System.out.println("重寫日志記錄功能") ;
  }
}

當(dāng)項(xiàng)目運(yùn)行時(shí)出現(xiàn)詭異的問題,既然打印了2次日志信息。

// 測試代碼
AnnotationConfigApplicationContext context = ... ;
PersonService ps = context.getBean(PersonService.class) ;
ps.queryById(1L) ;

執(zhí)行結(jié)果

重寫日志記錄功能
重寫日志記錄功能
查詢Person對象

雖然執(zhí)行了我們重寫的方法,但是日志確輸出了2遍。

通過debug分析

容器在啟動初始化解析@Aspect切面時(shí),在獲取切面類中的所有方法時(shí),會得到兩個(gè)方法(父類及子類重寫的)

圖片圖片

在這里的getAdvisorMethods方法返回了3個(gè)方法,其中2個(gè)是父類中的一個(gè)是子類重寫的方法

圖片圖片

那么接下來根據(jù)這2個(gè)方法就會生成對應(yīng)的Advisor對象。

圖片圖片

這也就是為什么重復(fù)的原因了。Spring并沒有判斷我當(dāng)前的這個(gè)通知是否是重寫父類的方法。

注:在Spring中@Aspect定義的切面最終都會轉(zhuǎn)換為Advisor對象,當(dāng)代理類在執(zhí)行時(shí)會遍歷所有符合添加的Advisor然后從中取出對應(yīng)的Advice(MethodInterceptor)對象。

既然知道了問題出現(xiàn)的原因,接下來就進(jìn)行解決該問題。

2. 解決問題

2.1 解決辦法1

我們可以在子類重寫的方法上再加上通知類型,將切入點(diǎn)設(shè)置的不匹配任何方法即可

@Before("execution(public * xxxooo())")
@Override
public void beforeLog(JoinPoint jp) {
  System.out.println("重寫日志記錄功能") ;
}

在這里重寫的方法上,將切入點(diǎn)重新寫,該切入點(diǎn)不會匹配任何的方法,這樣修改以后再次執(zhí)行

重寫日志記錄功能
查詢Person對象

正常執(zhí)行,沒有重復(fù)輸出日志。

2.2 解決辦法2

該方法需要將Spring版本升級到6.1.8,在該版本中解決了該問題。來看看在該版本中是如何解決的。

6.1.8版本

核心方法還是在上面debug是看到的getAdvisors方法中

public class ReflectiveAspectJAdvisorFactory {
  public List<Advisor> getAdvisors(...) {
    List<Advisor> advisors = new ArrayList<>();
    for (Method method : getAdvisorMethods(aspectClass)) {
      // 在這里加入了判斷(當(dāng)前方法是否與當(dāng)前切面中的方法相同)
      // 當(dāng)遍歷到這里的Method是父類中的方法時(shí),這里的getMostSpecificMethod
      // 方法就會判斷根據(jù)方法的名稱再從當(dāng)前切面類中獲取方法(那當(dāng)然就不同了)
      if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
        if (advisor != null) {
          advisors.add(advisor);
        }
      }
    }
  }
}

圖片圖片

在6.1.8版本中通過這種方式就排除了父類的方法。

其它版本

圖片圖片

低于6.1.8版本,都沒有相應(yīng)的判斷。

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2012-02-10 10:32:33

JavaSpring

2024-04-10 08:59:39

SpringAOP業(yè)務(wù)

2009-08-24 09:46:40

面向切面編程AOP

2022-02-16 13:46:40

Spring Aop代碼注解

2014-03-24 11:07:18

金山毒霸BUG驅(qū)動故障

2013-09-17 10:37:03

AOPAOP教程理解AOP

2011-04-26 09:33:04

SpringAOP

2022-04-22 15:03:14

Android漏洞網(wǎng)絡(luò)攻擊

2023-11-07 16:00:25

面向切面編程開發(fā)

2024-05-21 09:55:43

AspectOrientedAOP

2024-10-12 14:36:34

2023-09-04 08:36:19

SpringAop日志輸出

2020-10-16 09:40:18

順序Spring AOPHTTP

2009-06-19 13:28:30

Spring AOPSpring 2.0

2022-06-07 07:58:45

SpringSpring AOP

2010-06-11 17:08:05

2021-09-27 10:15:10

故障業(yè)務(wù)方電腦

2009-05-27 09:32:20

2025-04-28 09:15:56

2022-02-17 13:39:09

AOP接口方式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號