AOP(Aspect-Oriented Programming):面向切面的編程。OOP(Object-Oriented Programming)面向對象的編程。對於OOP咱們已經再熟悉不過了,對於AOP,可能咱們會以爲是一種新特性,其實AOP是對OOP的一種補充,OOP面向的是縱向編程,繼承、封裝、多態是其三大特性,而AOP是面向橫向的編程。spring
面向切面編程(AOP)經過提供另一種思考程序結構的途經來彌補面向對象編程(OOP)的不足。在OOP中模塊化的關鍵單元是類(classes),而在AOP中模塊化的單元則是切面。切面能對關注點進行模塊化,例如橫切多個類型和對象的事務管理。編程
AOP框架是Spring的一個重要組成部分。可是Spring IoC容器並不依賴於AOP,這意味着你有權利選擇是否使用AOP,AOP作爲Spring IoC容器的一個補充,使它成爲一個強大的中間件解決方案。數組
AOP在Spring Framework中的做用 提供聲明式企業服務,特別是爲了替代EJB聲明式服務。最重要的服務是聲明性事務管理(這個我想是AOP使用最多的一處了)。 容許用戶實現自定義切面,用AOP來完善OOP的使用。緩存
學習AOP,固然得先了解一下其衆多的概念性術語: 切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可使用基於模式)或者基於@Aspect註解的方式來實現。框架
鏈接點(Joinpoint):在程序執行過程當中某個特定的點,好比某方法調用的時候或者處理異常的時候。在Spring AOP中,一個鏈接點老是表示一個方法的執行。 通知(Advice):在切面的某個特定的鏈接點上執行的動做。其中包括了「around」、「before」和「after」等不一樣類型的通知(通知的類型將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器作通知模型,並維護一個以鏈接點爲中心的攔截器鏈。模塊化
切入點(Pointcut):匹配鏈接點的斷言。通知和一個切入點表達式關聯,並在知足這個切入點的鏈接點上運行(例如,當執行某個特定名稱的方法時)。切入點表達式如何和鏈接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。學習
引入(Introduction):用來給一個類型聲明額外的方法或屬性(也被稱爲鏈接類型聲明(inter-type declaration))。Spring容許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,你可使用引入來使一個bean實現IsModified接口,以便簡化緩存機制。 目標對象(Target Object): 被一個或者多個切面所通知的對象。也被稱作被通知(advised)對象。 既然Spring AOP是經過運行時代理實現的,這個對象永遠是一個被代理(proxied)對象。優化
AOP代理(AOP Proxy):AOP框架建立的對象,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理能夠是JDK動態代理或者CGLIB代理。 織入(Weaving):把切面鏈接到其它的應用程序類型或者對象上,並建立一個被通知的對象。這些能夠在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入。設計
前置通知(Before advice):在某鏈接點以前執行的通知,但這個通知不能阻止鏈接點以前的執行流程(除非它拋出一個異常)。 後置通知(After returning advice):在某鏈接點正常完成後執行的通知:例如,一個方法沒有拋出任何異常,正常返回。 異常通知(After throwing advice):在方法拋出異常退出時執行的通知。 最終通知(After (finally) advice):當某鏈接點退出的時候執行的通知(不管是正常返回仍是異常退出)。 環繞通知(Around Advice):包圍一個鏈接點的通知,如方法調用。這是最強大的一種通知類型。環繞通知能夠在方法調用先後完成自定義的行爲。它也會選擇是否繼續執行鏈接點或直接返回它本身的返回值或拋出異常來結束執行。代理
環繞通知是最經常使用的通知類型。和AspectJ同樣,Spring提供全部類型的通知,咱們推薦你使用盡量簡單的通知類型來實現須要的功能。例如,若是你只是須要一個方法的返回值來更新緩存,最好使用後置通知而不是環繞通知,儘管環繞通知也能完成一樣的事情。用最合適的通知類型可使得編程模型變得簡單,而且可以避免不少潛在的錯誤。好比,你不須要在JoinPoint上調用用於環繞通知的proceed()方法,就不會有調用的問題。
在這裏,基於@AspectJ的AOP我就很少寫了,由於我更青睞於Spring中使用ProxyFactoryBean建立AOP代理。
在Spring裏建立一個AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。 這個類對應用的切入點和通知提供了完整的控制能力(包括它們的應用順序)。像其它的FactoryBean實現同樣,ProxyFactoryBean引入了一個間接層。若是你定義一個名爲foo的ProxyFactoryBean, 引用foo的對象看到的將不是ProxyFactoryBean實例自己,而是一個ProxyFactoryBean實現裏getObject() 方法所建立的對象。 這個方法將建立一個AOP代理,它包裝了一個目標對象。
ProxyFactoryBean類自己也是一個JavaBean,其屬性主要有以下用途:
指定你但願代理的目標對象 指定是否使用CGLIB。
一些主要屬性從org.springframework.aop.framework.ProxyConfig裏繼承下來(這個類是Spring裏全部AOP代理工廠的父類)。這些主要屬性包括: proxyTargetClass:這個屬性爲true時,目標類自己被代理而不是目標類的接口。若是這個屬性值被設爲true,CGLIB代理將被建立。 optimize:用來控制經過CGLIB建立的代理是否使用激進的優化策略。 除非徹底瞭解AOP代理如何處理優化,不然不推薦用戶使用這個設置。目前這個屬性僅用於CGLIB代理; 對於JDK動態代理(缺省代理)無效。 frozen:若是一個代理配置是frozen的,就不容許對該配置進行修改。 這在簡單優化和不但願調用者在代理建立後操做代理(經過Advised接口) 時頗有用。缺省值爲false,便可以進行相似添加附加通知的操做。 exposeProxy:決定當前代理是否被暴露在一個ThreadLocal 中以便被目標對象訪問。若是目標對象須要獲取代理並且exposeProxy屬性被設爲 true,目標對象可使用AopContext.currentProxy()方法。
aopProxyFactory:使用AopProxyFactory的實現。這提供了一種方法來自定義是否使用動態代理,CGLIB或其它代理策略。 缺省實現將根據狀況選擇動態代理或者CGLIB。通常狀況下應該沒有使用這個屬性的須要;它是被設計來在Spring 1.1中添加新的代理類型的。
ProxyFactoryBean中須要說明的其它屬性包括: proxyInterfaces:須要代理的接口名的字符串數組。 若是沒有提供,將爲目標類使用一個CGLIB代理。
interceptorNames:Advisor的字符串數組,能夠包括攔截器或其它通知的名字。 順序是很重要的,排在前面的將被優先服務。就是說列表裏的第一個攔截器將可以第一個攔截調用。這裏的名字是當前工廠中bean的名字,包括父工廠中bean的名字。這裏你不能使用bean的引用由於這會致使ProxyFactoryBean忽略通知的單例設置。你能夠把一個攔截器的名字加上一個星號做爲後綴(*)。這將致使這個應用程序裏全部名字以星號以前部分開頭的通知器都被應用。
單例:工廠是否應該返回同一個對象,不論方法getObject()被調用的多頻繁。 多個FactoryBean實現都提供了這個方法。缺省值是true。 若是你但願使用有狀態的通知–例如,有狀態的mixin–能夠把單例屬性的值設置爲false來使用原型通知。
若是一個須要被代理的目標對象的類(後面將簡單地稱它爲目標類)沒有實現任何接口,那麼一個基於CGLIB的代理將被建立。 這是最簡單的場景,由於JDK代理是基於接口的,沒有接口意味着沒有使用JDK進行代理的可能.
若是ProxyFactoryBean的proxyTargetClass屬性被設爲true,那麼一個基於CGLIB的代理將建立。 這樣的規定是有意義的,遵循了最小驚訝法則(保證了設定的一致性)。甚至當ProxyFactoryBean的proxyInterfaces屬性被設置爲一個或者多個全限定接口名, 而proxyTargetClass屬性被設置爲true仍然將實際使用基於CGLIB的代理。
若是ProxyFactoryBean的proxyInterfaces屬性被設置爲一個或者多個全限定接口名,一個基於JDK的代理將被建立。 被建立的代理將實現全部在proxyInterfaces屬性裏被說明的接口; 若是目標類實現了所有在proxyInterfaces屬性裏說明的接口以及一些額外接口,返回的代理將只實現說明的接口而不會實現那些額外接口。
若是ProxyFactoryBean的proxyInterfaces屬性沒有被設置, 可是目標類實現了一個(或者更多)接口,那麼ProxyFactoryBean將自動檢測到這個目標類已經實現了至少一個接口, 一個基於JDK的代理將被建立。被實際代理的接口將是目標類所實現的所有接口; 實際上,這和在proxyInterfaces屬性中列出目標類實現的每一個接口的狀況是同樣的。 然而,這將顯著地減小工做量以及輸入錯誤的可能性。