1. 程式人生 > >Java後臺框架篇--Spring的AOP實現原理

Java後臺框架篇--Spring的AOP實現原理

Spring的AOP實現原理,醞釀了一些日子,寫部落格之前信心不是很足,所以重新閱讀了一邊AOP的實現核心程式碼,而且又從網上找了一些Spring Aop剖析的例子,但是發現掛羊頭買狗肉的太多,標題高大上,內容卻大部分都是比較淺顯的一些介紹,可能也是由於比較少人閱讀這部分的核心程式碼邏輯把,然後寫這部分介紹的人估計也是少之又少,不過說實話,Spring Aop的核心原理實現介紹確實不太好寫,裡面涉及的類之間的呼叫還是蠻多的,關係圖畫的太細的畫也很難畫,而且最重要的一點就是,如果對AOP的概念以及spring的xml的解析,標籤的解析,註解實現,還有java的代理,這些知識沒有好好的理解的話也不可能對AOP的實現詳細邏輯有一個好的理解。所以的話,建議是把這些前置知識都大概瞭解了,再來看這個AOP的實現,或者去閱讀原始碼,那樣的話學習 起來會容易的多。

學習原始碼的過程比較枯燥,尤其是spring比較嚴謹,呼叫層次比較多,沒有畫時序圖的話可能真的被繞暈,所以建議學的時候還是畫畫時序圖,然後跟著debug模式流程走一遍。

首先我們來熱熱身吧,先看看AOP的簡介,待會再進入主題,

AOP簡介

概念

切面(Aspect) :官方的抽象定義為“一個關注點的模組化,這個關注點可能會橫切多個物件”。
連線點(Joinpoint) :程式執行過程中的某一行為。
通知(Advice) :“切面”對於某個“連線點”所產生的動作。
切入點(Pointcut) :匹配連線點的斷言,在AOP中通知和一個切入點表示式關聯。
目標物件(Target Object) :被一個或者多個切面所通知的物件。
AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。

通知(Advice)型別


前置通知(Before advice) :在某連線點(JoinPoint)之前執行的通知,但這個通知不能阻止連線點前的執行。ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行宣告。
後通知(After advice) :當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行宣告。
返回後通知(After return advice) :在某連線點正常完成後執行的通知,不包括丟擲異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行宣告。
環繞通知(Around advice) :包圍一個連線點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的呼叫前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行宣告。
丟擲異常後通知(After throwing advice) : 在方法丟擲異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行宣告。

切入點表示式 :如execution(* com.spring.service.*.*(..))

特點

1、降低模組之間的耦合度

2、使系統容易擴充套件

3、更好的程式碼複用。

動態AOP使用示例

由於在之前的部落格,已經介紹過spring原始碼剖析(五)利用AOP實現自定義Spring註解 裡面有介紹到AOP 的簡單使用,相信大家要看這個AOP的原理的也對AOP的使用比較熟悉了,所以在這裡也不再重複展示了。

動態AOP自定義標籤

我之前的部落格中有說到,如何自定義Spring標籤的,以及自定義Spring標籤的大概解析流程,其實這裡的AOP的標籤的定義也和之前的邏輯類似,先上時序圖把:

時序圖


流程說明

1)AOP標籤的定義解析劉徹骨肯定是從NamespaceHandlerSupport的實現類開始解析的,這個實現類就是AopNamespaceHandler。至於為什麼會是從NamespaceHandlerSupport的實現類開始解析的,這個的話我想讀者可以去在回去看看Spring自定義標籤的解析流程,裡面說的比較詳細。

2)要啟用AOP,我們一般會在Spring裡面配置<aop:aspectj-autoproxy/>  ,所以在配置檔案中在遇到aspectj-autoproxy標籤的時候我們會採用AspectJAutoProxyBeanDefinitionParser解析器

3)進入AspectJAutoProxyBeanDefinitionParser解析器後,呼叫AspectJAutoProxyBeanDefinitionParser已覆蓋BeanDefinitionParser的parser方法,然後parser方法把請求轉交給了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去處理

4)進入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法後,先呼叫AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,裡面在轉發呼叫給registerOrEscalateApcAsRequired,註冊或者升級AnnotationAwareAspectJAutoProxyCreator類。對於AOP的實現,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根據@point註解定義的切點來代理相匹配的bean。

5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法處理完成之後,接下來會呼叫useClassProxyingIfNecessary() 處理proxy-target-class以及expose-proxy屬性。如果將proxy-target-class設定為true的話,那麼會強制使用CGLIB代理,否則使用jdk動態代理,expose-proxy屬性是為了解決有時候目標物件內部的自我呼叫無法實現切面增強。

6)最後的呼叫registerComponentIfNecessary 方法,註冊組建並且通知便於監聽器做進一步處理。

建立AOP代理

上面說到AOP的核心邏輯是在AnnotationAwareAspectJAutoProxyCreator類裡面實現,那麼我們先來看看這個類的層次關係


我們可以看到這個類實現了BeanPostProcessor介面,那就意味著這個類在spring載入例項化前會呼叫postProcessAfterInitialization方法,對於AOP的邏輯也是由此開始的。

時序圖


流程說明

1)spring 容器啟動,每個bean的例項化之前都會先經過AbstractAutoProxyCreator類的postProcessAfterInitialization()這個方法,然後接下來是呼叫wrapIfNecessary方法。

  1. /** 
  2.  * Create a proxy with the configured interceptors if the bean is 
  3.  * identified as one to proxy by the subclass. 
  4.  * @see #getAdvicesAndAdvisorsForBean 
  5.  */
  6. public Object <strong>postProcessAfterInitialization</strong>(Object bean, String beanName) throws BeansException {  
  7.     if (bean != null) {  
  8.         Object cacheKey = getCacheKey(bean.getClass(), beanName);  
  9.         if (!this.earlyProxyReferences.containsKey(cacheKey)) {  
  10.             return wrapIfNecessary(bean, beanName, cacheKey);  
  11.         }  
  12.     }  
  13.     return bean;  
  14. }  

2)進入wrapIfNecessary方法後,我們直接看重點實現邏輯的方法getAdvicesAndAdvisorsForBean,這個方法會提取當前bean 的所有增強方法,然後獲取到適合的當前bean 的增強方法,然後對增強方法進行排序,最後返回。

  1. /** 
  2.      * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. 
  3.      * @param bean the raw bean instance 
  4.      * @param beanName the name of the bean 
  5.      * @param cacheKey the cache key for metadata access 
  6.      * @return a proxy wrapping the bean, or the raw bean instance as-is 
  7.      */
  8.     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {  
  9.         if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {  
  10.             return bean;  
  11.         }  
  12.         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {  
  13.             return bean;  
  14.         }  
  15.         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {  
  16.             this.advisedBeans.put(cacheKey, Boolean.FALSE);  
  17.             return bean;  
  18.         }  
  19.         // Create proxy if we have advice.  
  20.         Object[] specificInterceptors = <strong>getAdvicesAndAdvisorsForBean</strong>(bean.getClass(), beanName, null);  
  21.         if (specificInterceptors != DO_NOT_PROXY) {  
  22.             this.advisedBeans.put(cacheKey, Boolean.TRUE);  
  23.             Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));  
  24.             this.proxyTypes.put(cacheKey, proxy.getClass());  
  25.             return proxy;  
  26.         }  
  27.         this.advisedBeans.put(cacheKey, Boolean.FALSE);  
  28.         return bean;  
  29.     }  
3)獲取到當前bean的增強方法後,便呼叫createProxy方法,建立代理。先建立代理工廠proxyFactory,然後獲取當前bean 的增強器advisors,把當前獲取到的增強器新增到代理工廠proxyFactory,然後設定當前的代理工的代理目標物件為當前bean,最後根據配置建立JDK的動態代理工廠,或者CGLIB的動態代理工廠,然後返回proxyFactory
  1. /** 
  2.      * Create an AOP proxy for the given bean. 
  3.      * @param beanClass the class of the bean 
  4.      * @param beanName the name of the bean 
  5.      * @param specificInterceptors the set of interceptors that is 
  6.      * specific to this bean (may be empty, but not null) 
  7.      * @param targetSource the TargetSource for the proxy, 
  8.      * already pre-configured to access the bean 
  9.      * @return the AOP proxy for the bean 
  10.      * @see #buildAdvisors 
  11.      */
  12.     protected Object createProxy(  
  13.             Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {  
  14.         ProxyFactory proxyFactory = new ProxyFactory();  
  15.         // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
  16.         proxyFactory.copyFrom(this);  
  17.         if (!shouldProxyTargetClass(beanClass, beanName)) {  
  18.             // Must allow for introductions; can't just set interfaces to
  19.             // the target's interfaces only.
  20.             Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);  
  21.             for (Class<?> targetInterface : targetInterfaces) {  
  22.                 proxyFactory.addInterface(targetInterface);  
  23.             }  
  24.         }  
  25.         Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);  
  26.         for (Advisor advisor : advisors) {  
  27.             proxyFactory.addAdvisor(advisor);  
  28.         }  
  29.         proxyFactory.<strong>setTargetSource</strong>(targetSource);  
  30.         customizeProxyFactory(proxyFactory);  
  31.         proxyFactory.setFrozen(this.freezeProxy);  
  32.         if (advisorsPreFiltered()) {  
  33.             proxyFactory.setPreFiltered(true);  
  34.         }  
  35.         return proxyFactory.getProxy(this.proxyClassLoader);  
  36.     }  

AOP動態代理執行

關於AOP的動態代理執行,有兩種主要的方式JDK的動態代理和CGLIB的動態代理,如果對這兩種代理不是很熟悉的話,建議先去看看我之前寫的一篇部落格 java代理(靜態代理和jdk動態代理以及cglib代理) 裡面有介紹這兩種代理的使用和實現方式。 接下來,我們先來看看AOP動態代理的實現選擇方式,先上核心實現程式碼:
  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  2.     if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  
  3.         Class targetClass = config.getTargetClass();  
  4.         if (targetClass == null

    相關推薦

    Java後臺框架--Spring的AOP實現原理

    Spring的AOP實現原理,醞釀了一些日子,寫部落格之前信心不是很足,所以重新閱讀了一邊AOP的實現核心程式碼,而且又從網上找了一些Spring Aop剖析的例子,但是發現掛羊頭買狗肉的太多,標題高大上,內容卻大部分都是比較淺顯的一些介紹,可能也是由於比較少人閱讀這

    Java後臺框架--spring websocket 和stomp實現訊息功能

    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <html> <head> <title>Home</title> <spring:

    Java後臺框架--Spring與Restful風格API介面開發

    Restful風格的API是一種軟體架構風格,設計風格而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和伺服器互動類的軟體。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。 在Restful風格中,使用者請求的url使用同一個url而用請求方式:get,post,

    Java後臺框架--Spring的三種配置方式

    1、Explicit configuration in XML:顯示的XML配置。      優點:      1)XML配置方式進一步降低了耦合,使得應用更加容易擴充套件,即使對配置檔案進一步修改也不需要工程進行修改和重新編譯。      2)在處理大的業務量的時候,用X

    Java後臺框架--Spring單元測試中的H2資料庫

    H2資料庫是一種由Java編寫的,極小,速度極快,可嵌入式的資料庫。非常適合用在單元測試等資料不需要儲存的場景下面。 以下時其官網的介紹: {% blockquote h2 http://www.h2database.com/html/main.html h2 %}Wel

    Java後臺框架--Spring之快取

    public class TestCache { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("config/spr

    Java後臺框架--Spring之WebService入門

    前置技能 這個技能必須點一級,以便快速配置專案。 本文實際上是我學習Spring的過程中搬的官網上的demo,使用maven配置專案。 ② jdk 1.8+   該服務demo需要在jdk1.8+的環境下執行 新建專案,配置依賴檔案 ① 安裝好eclipse

    spring框架學習筆記4:SpringAOP實現原理

    odin 就是 sets 使用 point 攔截 ceo oca ssl AOP AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入

    Java 後臺框架源碼 springmvc spring mybatis SSM 有代碼生成器

    springmvc 框架 源碼 ehcache mybatis 官網 http://www.fhadmin.org/A代碼編輯器,在線模版編輯,仿開發工具編輯器,pdf在線預覽,文件轉換編碼B 集成代碼生成器 [正反雙向](單表、主表、明細表、樹形表,快速開發利器)+快速表單構建器 fr

    這麽說吧,java線程池的實現原理其實很簡單

    arr nan ads stop shc 線程異常 fixed 響應 submit 好處 : 線程是稀缺資源,如果被無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,合理的使用線程池對線程進行統一分配、調優和監控,有以下好處: 1、降低資源消耗; 2、提高響應速度;

    java後臺框架 springmvc整合mybatis框架源碼 java圖片爬蟲 bootstra

    java 官網 http://www.fhadmin.org/A代碼編輯器,在線模版編輯,仿開發工具編輯器,pdf在線預覽,文件轉換編碼B 集成代碼生成器 [正反雙向](單表、主表、明細表、樹形表,快速開發利器)+快速表單構建器freemaker模版技術 ,0個代碼不用寫,生成完整的一個模塊,帶頁面、建表sq

    java後臺框架 springmvc整合mybatis框架源碼 java圖片爬蟲 bootstrap

    支持 java圖片 pie ext eem 異步操作 分配 自動 擴展 A代碼編輯器,在線模版編輯,仿開發工具編輯器,pdf在線預覽,文件轉換編碼B 集成代碼生成器 [正反雙向](單表、主表、明細表、樹形表,快速開發利器)+快速表單構建器freemaker模版技術 ,0個代

    java 後臺框架 支持APP接口調用 APP後臺 手機後臺框架java springmvc myb

    圖片裁剪 穩定 line 爬取圖片 均可 druid 富文本編輯 文本框 純java A代碼編輯器,在線模版編輯,仿開發工具編輯器,pdf在線預覽,文件轉換編碼B 集成代碼生成器 [正反雙向](單表、主表、明細表、樹形表,快速開發利器)+快速表單構建器freemaker模版

    深入分析java線程池的實現原理

    51cto 產生 read 記錄 epo 內部實現 9.png 方法 單位 前言 線程是稀缺資源,如果被無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,合理的使用線程池對線程進行統一分配、調優和監控,有以下好處:1、降低資源消耗;2、提高響應速度;3、提高線程的可管

    springAOP實現原理

    all sed gis instance bubuko beans exce creator annotate spring AOP實現原理, spring 會在初始化的時候,創建一個BeanPostProcessor(AnnotationAwareAspectJAutoP

    併發程式設計的藝術——第二章Java併發機制的底層實現原理

    第一節 volatile的應用 定義:Java程式語言允許執行緒訪問共享變數,為了確保共享變數能被準確和一致地更新,執行緒應該確保通過排他鎖單獨獲得這個變數。 為了提高處理速度,處理器不直接和記憶體進行通訊,而是先將系統記憶體的資料讀到內部快取後再進行操作 在多處理器下, 為了保證各個處

    二、Java併發機制的底層實現原理

    Java程式碼編譯後變成java位元組碼,位元組碼被類載入器載入到JVM裡,JVM執行位元組碼,最終需要轉化為彙編指令在CPU上執行,java中所使用的併發機制依賴於JVM的實現和CPU的執行。 2.1 volatile的應用 在多執行緒併發程式設計中,synchronized和v

    java併發機制的底層實現原理(一):volatile深入分析

         java程式碼最終會被類載入器載入到JVM中,然後轉化為彙編指令在CPU上執行。java中所使用的併發機制依賴於JVM的實現和CPU的指令。 1.volatile的應用 volatile是一個輕量級的synchronize,它保證了共享變數的可見性,確保了所有執

    Java底層之HashMap底層實現原理

    HashMap簡介       HashMap 是一個散列表,它儲存的內容是鍵值對(key-value)對映。 HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。 HashMap 的實現不是同步的,

    Java網際網路程式設計——深入分析java執行緒池的實現原理

    月亮姨的嘮叨: 執行緒是稀缺資源,如果被無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,合理的使用執行緒池對執行緒進行統一分配、調優和監控,有以下好處: 1、降低資源消耗; 2、提高響應速度; 3、提高執行緒的可管理性。 Java1.5中引入的Executo