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方法。
- /**
- * Create a proxy with the configured interceptors if the bean is
- * identified as one to proxy by the subclass.
- * @see #getAdvicesAndAdvisorsForBean
- */
- public Object <strong>postProcessAfterInitialization</strong>(Object bean, String beanName) throws BeansException {
- if (bean != null) {
- Object cacheKey = getCacheKey(bean.getClass(), beanName);
- if (!this.earlyProxyReferences.containsKey(cacheKey)) {
- return wrapIfNecessary(bean, beanName, cacheKey);
- }
- }
- return bean;
- }
2)進入wrapIfNecessary方法後,我們直接看重點實現邏輯的方法getAdvicesAndAdvisorsForBean,這個方法會提取當前bean 的所有增強方法,然後獲取到適合的當前bean 的增強方法,然後對增強方法進行排序,最後返回。
- /**
- * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
- * @param bean the raw bean instance
- * @param beanName the name of the bean
- * @param cacheKey the cache key for metadata access
- * @return a proxy wrapping the bean, or the raw bean instance as-is
- */
- protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
- if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
- return bean;
- }
- if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
- return bean;
- }
- if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
- this.advisedBeans.put(cacheKey, Boolean.FALSE);
- return bean;
- }
- // Create proxy if we have advice.
- Object[] specificInterceptors = <strong>getAdvicesAndAdvisorsForBean</strong>(bean.getClass(), beanName, null);
- if (specificInterceptors != DO_NOT_PROXY) {
- this.advisedBeans.put(cacheKey, Boolean.TRUE);
- Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
- this.proxyTypes.put(cacheKey, proxy.getClass());
- return proxy;
- }
- this.advisedBeans.put(cacheKey, Boolean.FALSE);
- return bean;
- }
- /**
- * Create an AOP proxy for the given bean.
- * @param beanClass the class of the bean
- * @param beanName the name of the bean
- * @param specificInterceptors the set of interceptors that is
- * specific to this bean (may be empty, but not null)
- * @param targetSource the TargetSource for the proxy,
- * already pre-configured to access the bean
- * @return the AOP proxy for the bean
- * @see #buildAdvisors
- */
- protected Object createProxy(
- Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
- ProxyFactory proxyFactory = new ProxyFactory();
- // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
- proxyFactory.copyFrom(this);
- if (!shouldProxyTargetClass(beanClass, beanName)) {
- // Must allow for introductions; can't just set interfaces to
- // the target's interfaces only.
- Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
- for (Class<?> targetInterface : targetInterfaces) {
- proxyFactory.addInterface(targetInterface);
- }
- }
- Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
- for (Advisor advisor : advisors) {
- proxyFactory.addAdvisor(advisor);
- }
- proxyFactory.<strong>setTargetSource</strong>(targetSource);
- customizeProxyFactory(proxyFactory);
- proxyFactory.setFrozen(this.freezeProxy);
- if (advisorsPreFiltered()) {
- proxyFactory.setPreFiltered(true);
- }
- return proxyFactory.getProxy(this.proxyClassLoader);
- }
AOP動態代理執行
關於AOP的動態代理執行,有兩種主要的方式JDK的動態代理和CGLIB的動態代理,如果對這兩種代理不是很熟悉的話,建議先去看看我之前寫的一篇部落格 java代理(靜態代理和jdk動態代理以及cglib代理) 裡面有介紹這兩種代理的使用和實現方式。 接下來,我們先來看看AOP動態代理的實現選擇方式,先上核心實現程式碼:- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class targetClass = config.getTargetClass();
-
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