溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring AOP實(shí)現(xiàn)原理的示例分析

發(fā)布時(shí)間:2021-08-06 10:53:27 來(lái)源:億速云 閱讀:152 作者:小新 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)Spring AOP實(shí)現(xiàn)原理的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

什么是AOP

AOP(Aspect-OrientedProgramming,面向方面編程),可以說(shuō)是OOP(Object-Oriented Programing,面向?qū)ο缶幊蹋┑难a(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP則顯得無(wú)能為力。也就是說(shuō),OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。對(duì)于其他類型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無(wú)關(guān)的代碼被稱為橫切(cross-cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。 

而AOP技術(shù)則恰恰相反,它利用一種稱為“橫切”的技術(shù),剖解開封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”,即方面。所謂“方面”,簡(jiǎn)單地說(shuō),就是將那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。AOP代表的是一個(gè)橫向的關(guān)系,如果說(shuō)“對(duì)象”是一個(gè)空心的圓柱體,其中封裝的是對(duì)象的屬性和行為;那么面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開,以獲得其內(nèi)部的消息。而剖開的切面,也就是所謂的“方面”了。然后它又以巧奪天功的妙手將這些剖開的切面復(fù)原,不留痕跡。 

使用“橫切”技術(shù),AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個(gè)特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處都基本相似。比如權(quán)限認(rèn)證、日志、事務(wù)處理。Aop 的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開來(lái)。正如Avanade公司的高級(jí)方案構(gòu)架師Adam Magee所說(shuō),AOP的核心思想就是“將應(yīng)用程序中的商業(yè)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離?!?nbsp;

實(shí)現(xiàn)AOP的技術(shù),主要分為兩大類:一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式,對(duì)該消息進(jìn)行裝飾,以取代原有對(duì)象行為的執(zhí)行;二是采用靜態(tài)織入的方式,引入特定的語(yǔ)法創(chuàng)建“方面”,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼。

AOP使用場(chǎng)景

AOP用來(lái)封裝橫切關(guān)注點(diǎn),具體可以在下面的場(chǎng)景中使用: 

Authentication 權(quán)限
Caching 緩存
Context passing 內(nèi)容傳遞
Error handling 錯(cuò)誤處理
Lazy loading 懶加載
Debugging  調(diào)試
logging, tracing, profiling and monitoring 記錄跟蹤 優(yōu)化 校準(zhǔn)
Performance optimization 性能優(yōu)化
Persistence  持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務(wù)

AOP相關(guān)概念

方面(Aspect):一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)實(shí)現(xiàn)可能另外橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)很好的橫切關(guān)注點(diǎn)例子。方面用Spring的 Advisor或攔截器實(shí)現(xiàn)。 

連接點(diǎn)(Joinpoint): 程序執(zhí)行過(guò)程中明確的點(diǎn),如方法的調(diào)用或特定的異常被拋出。 

通知(Advice): 在特定的連接點(diǎn),AOP框架執(zhí)行的動(dòng)作。各種類型的通知包括“around”、“before”和“throws”通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護(hù)一個(gè)“圍繞”連接點(diǎn)的攔截器鏈。Spring中定義了四個(gè)advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice 

切入點(diǎn)(Pointcut): 指定一個(gè)通知將被引發(fā)的一系列連接點(diǎn)的集合。AOP框架必須允許開發(fā)者指定切入點(diǎn):例如,使用正則表達(dá)式。 Spring定義了Pointcut接口,用來(lái)組合MethodMatcher和ClassFilter,可以通過(guò)名字很清楚的理解, MethodMatcher是用來(lái)檢查目標(biāo)類的方法是否可以被應(yīng)用此通知,而ClassFilter是用來(lái)檢查Pointcut是否應(yīng)該應(yīng)用到目標(biāo)類上 

引入(Introduction): 添加方法或字段到被通知的類。 Spring允許引入新的接口到任何被通知的對(duì)象。例如,你可以使用一個(gè)引入使任何對(duì)象實(shí)現(xiàn) IsModified接口,來(lái)簡(jiǎn)化緩存。Spring中要使用Introduction, 可有通過(guò)DelegatingIntroductionInterceptor來(lái)實(shí)現(xiàn)通知,通過(guò)DefaultIntroductionAdvisor來(lái)配置Advice和代理類要實(shí)現(xiàn)的接口 

目標(biāo)對(duì)象(Target Object): 包含連接點(diǎn)的對(duì)象。也被稱作被通知或被代理對(duì)象。POJO 

AOP代理(AOP Proxy): AOP框架創(chuàng)建的對(duì)象,包含通知。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。 

織入(Weaving): 組裝方面來(lái)創(chuàng)建一個(gè)被通知對(duì)象。這可以在編譯時(shí)完成(例如使用AspectJ編譯器),也可以在運(yùn)行時(shí)完成。Spring和其他純Java AOP框架一樣,在運(yùn)行時(shí)完成織入。

Spring AOP組件

下面這種類圖列出了Spring中主要的AOP組件

Spring AOP實(shí)現(xiàn)原理的示例分析

如何使用Spring AOP

可以通過(guò)配置文件或者編程的方式來(lái)使用Spring AOP。 

配置可以通過(guò)xml文件來(lái)進(jìn)行,大概有四種方式:
1. 配置ProxyFactoryBean,顯式地設(shè)置advisors, advice, target等
2. 配置AutoProxyCreator,這種方式下,還是如以前一樣使用定義的bean,但是從容器中獲得的其實(shí)已經(jīng)是代理對(duì)象
3. 通過(guò)<aop:config>來(lái)配置
4. 通過(guò)<aop: aspectj-autoproxy>來(lái)配置,使用AspectJ的注解來(lái)標(biāo)識(shí)通知及切入點(diǎn) 

也可以直接使用ProxyFactory來(lái)以編程的方式使用Spring AOP,通過(guò)ProxyFactory提供的方法可以設(shè)置target對(duì)象, advisor等相關(guān)配置,最終通過(guò) getProxy()方法來(lái)獲取代理對(duì)象 

具體使用的示例可以google. 這里略去

Spring AOP代理對(duì)象的生成

Spring提供了兩種方式來(lái)生成代理對(duì)象: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對(duì)象的配置來(lái)決定。默認(rèn)的策略是如果目標(biāo)類是接口,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來(lái)生成代理。下面我們來(lái)研究一下Spring如何使用JDK來(lái)生成代理對(duì)象,具體的生成代碼放在JdkDynamicAopProxy這個(gè)類中,直接上相關(guān)代碼:

/** 
  * <ol> 
  * <li>獲取代理類要實(shí)現(xiàn)的接口,除了Advised對(duì)象中配置的,還會(huì)加上SpringProxy, Advised(opaque=false) 
  * <li>檢查上面得到的接口中有沒有定義 equals或者h(yuǎn)ashcode的接口 
  * <li>調(diào)用Proxy.newProxyInstance創(chuàng)建代理對(duì)象 
  * </ol> 
  */ 
  public Object getProxy(ClassLoader classLoader) { 
    if (logger.isDebugEnabled()) { 
      logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource()); 
    } 
    Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised); 
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 
}

那這個(gè)其實(shí)很明了,注釋上我也已經(jīng)寫清楚了,不再贅述。 

下面的問題是,代理對(duì)象生成了,那切面是如何織入的?
我們知道InvocationHandler是JDK動(dòng)態(tài)代理的核心,生成的代理對(duì)象的方法調(diào)用都會(huì)委托到InvocationHandler.invoke()方法。而通過(guò)JdkDynamicAopProxy的簽名我們可以看到這個(gè)類其實(shí)也實(shí)現(xiàn)了InvocationHandler,下面我們就通過(guò)分析這個(gè)類中實(shí)現(xiàn)的invoke()方法來(lái)具體看下Spring AOP是如何織入切面的。

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable { 
    MethodInvocation invocation = null; 
    Object oldProxy = null; 
    boolean setProxyContext = false; 
  
    TargetSource targetSource = this.advised.targetSource; 
    Class targetClass = null; 
    Object target = null; 
  
    try { 
      //eqauls()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法 
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){ 
        return (equals(args[0])? Boolean.TRUE : Boolean.FALSE); 
      } 
  
      //hashCode()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法 
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){ 
        return newInteger(hashCode()); 
      } 
  
      //Advised接口或者其父接口中定義的方法,直接反射調(diào)用,不應(yīng)用通知 
      if (!this.advised.opaque &&method.getDeclaringClass().isInterface() 
          &&method.getDeclaringClass().isAssignableFrom(Advised.class)) { 
        // Service invocations onProxyConfig with the proxy config... 
        return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args); 
      } 
  
      Object retVal = null; 
  
      if (this.advised.exposeProxy) { 
        // Make invocation available ifnecessary. 
        oldProxy = AopContext.setCurrentProxy(proxy); 
        setProxyContext = true; 
      } 
  
      //獲得目標(biāo)對(duì)象的類 
      target = targetSource.getTarget(); 
      if (target != null) { 
        targetClass = target.getClass(); 
      } 
  
      //獲取可以應(yīng)用到此方法上的Interceptor列表 
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass); 
  
      //如果沒有可以應(yīng)用到此方法的通知(Interceptor),此直接反射調(diào)用 method.invoke(target, args) 
      if (chain.isEmpty()) { 
        retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); 
      } else { 
        //創(chuàng)建MethodInvocation 
        invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 
        retVal = invocation.proceed(); 
      } 
  
      // Massage return value if necessary. 
      if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy) 
          &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { 
        // Special case: it returned"this" and the return type of the method 
        // is type-compatible. Notethat we can't help if the target sets 
        // a reference to itself inanother returned object. 
        retVal = proxy; 
      } 
      return retVal; 
    } finally { 
      if (target != null && !targetSource.isStatic()) { 
        // Must have come fromTargetSource. 
        targetSource.releaseTarget(target); 
      } 
      if (setProxyContext) { 
        // Restore old proxy. 
        AopContext.setCurrentProxy(oldProxy); 
      } 
    } 
  }

主流程可以簡(jiǎn)述為:獲取可以應(yīng)用到此方法上的通知鏈(Interceptor Chain),如果有,則應(yīng)用通知,并執(zhí)行joinpoint; 如果沒有,則直接反射執(zhí)行joinpoint。而這里的關(guān)鍵是通知鏈?zhǔn)侨绾潍@取的以及它又是如何執(zhí)行的,下面逐一分析下。 

首先,從上面的代碼可以看到,通知鏈?zhǔn)峭ㄟ^(guò)Advised.getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)獲取的,我們來(lái)看下這個(gè)方法的實(shí)現(xiàn):

public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { 
          MethodCacheKeycacheKey = new MethodCacheKey(method); 
          List<Object>cached = this.methodCache.get(cacheKey); 
          if(cached == null) { 
              cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( 
                        this,method, targetClass); 
              this.methodCache.put(cacheKey,cached); 
          } 
          returncached; 
     }

可以看到實(shí)際的獲取工作其實(shí)是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)完成的,獲取到的結(jié)果會(huì)被緩存。

下面來(lái)分析下這個(gè)方法的實(shí)現(xiàn):

/** 
  * 從提供的配置實(shí)例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor, 
  * 則判斷此Advisor能否應(yīng)用到目標(biāo)類targetClass上.如果是PointcutAdvisor,則判斷 
  * 此Advisor能否應(yīng)用到目標(biāo)方法method上.將滿足條件的Advisor通過(guò)AdvisorAdaptor轉(zhuǎn)化成Interceptor列表返回. 
  */ 
  publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) { 
    // This is somewhat tricky... we have to process introductions first, 
    // but we need to preserve order in the ultimate list. 
    List interceptorList = new ArrayList(config.getAdvisors().length); 
  
    //查看是否包含IntroductionAdvisor 
    boolean hasIntroductions = hasMatchingIntroductions(config,targetClass); 
  
    //這里實(shí)際上注冊(cè)一系列AdvisorAdapter,用于將Advisor轉(zhuǎn)化成MethodInterceptor 
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); 
  
    Advisor[] advisors = config.getAdvisors(); 
    for (int i = 0; i <advisors.length; i++) { 
      Advisor advisor = advisors[i]; 
      if (advisor instanceof PointcutAdvisor) { 
        // Add it conditionally. 
        PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor; 
        if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) { 
          //TODO: 這個(gè)地方這兩個(gè)方法的位置可以互換下 
          //將Advisor轉(zhuǎn)化成Interceptor 
          MethodInterceptor[]interceptors = registry.getInterceptors(advisor); 
  
          //檢查當(dāng)前advisor的pointcut是否可以匹配當(dāng)前方法 
          MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher(); 
  
          if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) { 
            if(mm.isRuntime()) { 
              // Creating a newobject instance in the getInterceptors() method 
              // isn't a problemas we normally cache created chains. 
              for (intj = 0; j < interceptors.length; j++) { 
                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm)); 
              } 
            } else { 
              interceptorList.addAll(Arrays.asList(interceptors)); 
            } 
          } 
        } 
      } else if (advisor instanceof IntroductionAdvisor){ 
        IntroductionAdvisor ia =(IntroductionAdvisor) advisor; 
        if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) { 
          Interceptor[] interceptors= registry.getInterceptors(advisor); 
          interceptorList.addAll(Arrays.asList(interceptors)); 
        } 
      } else { 
        Interceptor[] interceptors =registry.getInterceptors(advisor); 
        interceptorList.addAll(Arrays.asList(interceptors)); 
      } 
    } 
    return interceptorList; 
}

 

這個(gè)方法執(zhí)行完成后,Advised中配置能夠應(yīng)用到連接點(diǎn)或者目標(biāo)類的Advisor全部被轉(zhuǎn)化成了MethodInterceptor. 

接下來(lái)我們?cè)倏聪碌玫降臄r截器鏈?zhǔn)窃趺雌鹱饔玫摹?br/>

 if (chain.isEmpty()) { 
        retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); 
      } else { 
        //創(chuàng)建MethodInvocation 
        invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 
        retVal = invocation.proceed(); 
      }

從這段代碼可以看出,如果得到的攔截器鏈為空,則直接反射調(diào)用目標(biāo)方法,否則創(chuàng)建MethodInvocation,調(diào)用其proceed方法,觸發(fā)攔截器鏈的執(zhí)行,來(lái)看下具體代碼

public Object proceed() throws Throwable { 
    // We start with an index of -1and increment early. 
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) { 
      //如果Interceptor執(zhí)行完了,則執(zhí)行joinPoint 
      return invokeJoinpoint(); 
    } 
  
    Object interceptorOrInterceptionAdvice = 
      this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 
     
    //如果要?jiǎng)討B(tài)匹配joinPoint 
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){ 
      // Evaluate dynamic method matcher here: static part will already have 
      // been evaluated and found to match. 
      InterceptorAndDynamicMethodMatcher dm = 
        (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; 
      //動(dòng)態(tài)匹配:運(yùn)行時(shí)參數(shù)是否滿足匹配條件 
      if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) { 
        //執(zhí)行當(dāng)前Intercetpor 
        returndm.interceptor.invoke(this); 
      } 
      else { 
        //動(dòng)態(tài)匹配失敗時(shí),略過(guò)當(dāng)前Intercetpor,調(diào)用下一個(gè)Interceptor 
        return proceed(); 
      } 
    } 
    else { 
      // It's an interceptor, so we just invoke it: The pointcutwill have 
      // been evaluated statically before this object was constructed. 
      //執(zhí)行當(dāng)前Intercetpor 
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 
    } 
}

代碼也比較簡(jiǎn)單,這里不再贅述。

關(guān)于“Spring AOP實(shí)現(xiàn)原理的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI