除了依賴注入(DI),Spring框架提供的另外一個核心功能是對面向方面的編程(AOP)的支持。 AOP一般被稱爲實現橫切關注點的工具。橫切關注點一詞是指應用程序中的邏輯不能與應用程序的其他部分分離並有效模塊化的地方,而且可能致使代碼重複和緊密耦合。經過使用AOP模塊化單個邏輯(即關注點),能夠將它們應用於應用程序的許多部分,而無需複製代碼或建立硬依賴關係。日誌記錄和安全性是許多應用程序中橫切關注點的典型示例。AOP是對面向對象編程(OOP)的補充,而不是與之競爭。 OOP很是擅長解決咱們做爲程序員遇到的各類問題。鑑於AOP能夠在OOP之上運行,所以幾乎不可能單獨使用AOP來開發整個應用程序,儘管使用OOP固然能夠開發整個應用程序,可是經過使用AOP解決涉及橫切邏輯的某些問題,能更本身聰明地進行工做。java
AOP基礎知識程序員
AOP的類型spring
Spring AOP體系結構編程
使用Spring AOP安全
AOP框架服務架構
集成AspectJ框架
與大多數技術同樣,AOP帶有其本身的特定概念和術語集,所以瞭解它們的含義很重要。如下是AOP的核心概念:ide
鏈接點(Joinpoint):鏈接點是在應用程序執行期間定義明確的點。鏈接點的典型示例包括對方法的調用,方法調用自己,類初始化和對象實例化。鏈接點是AOP的核心概念,它定義了應用程序中的點,能夠在這些點上使用AOP插入其餘邏輯。模塊化
加強(Advice):在特定聯接點執行的代碼是Advice,由類中的方法定義。Advice有多種類型,例如以前,在鏈接點以前執行,以後,在鏈接點以後執行。(爲何不是翻譯成通知?過AOP將橫切關注功能加到原有的業務邏輯上,這就是對原有業務邏輯的一種加強,這種加強能夠是前置加強、後置加強、返回後加強、拋異常時加強和包圍型加強。)工具
切點(Pointcut):切入點是用於定義什麼時候執行建議的聯接點的集合。經過建立切入點,您能夠對如何將建議應用於應用程序中的組件進行精細控制。如前所述,典型的鏈接點是方法調用,或特定類中全部方法調用的集合。一般,您能夠在複雜的關係中編寫切入點,以在執行建議時進一步限制。
切面(Aspect):切面是封裝在類中的建議和切入點的組合。這種結合產生了應包含在應用程序中以及應在何處執行的邏輯的定義。
織入(Weaving):這是在適當的時候將各切面插入應用程序代碼中的過程。對於編譯時AOP解決方案,這種編織一般是在構建時完成的。一樣,對於運行時AOP解決方案,編織過程在運行時動態執行。 AspectJ支持另外一種編織機制,稱爲加載時間編織(LTW),在該機制中,它攔截底層的JVM類加載器,並在由類加載器加載字節碼時提供對字節碼的編織。
目標(Target):經過AOP流程修改了執行流程的對象稱爲目標對象。一般,你會看到目標對象,即建議對象。
引介(Introduction):這是能夠經過向對象引入其餘方法或字段來修改其結構的過程。可使用引介AOP使任何對象實現特定的接口,而無需對象的類顯式實現該接口。這樣,即便一個業務類本來沒有實現某個接口,經過引介功能,能夠動態的爲該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。
AOP有兩種不一樣的類型:靜態和動態。它們之間的區別其實是織造過程的發生點以及該過程的實現方式。
在靜態AOP中,編織過程構成了應用程序構建過程當中的另外一步驟。用Java術語,能夠經過修改應用程序的實際字節碼,並根據須要更改和擴展應用程序代碼,從而在靜態AOP實現中實現編織過程。這是完成編織過程的一種很好的方式,由於最終結果只是Java字節碼,而且您在運行時不會執行任何特殊技巧來肯定什麼時候應執行建議。這種機制的缺點是,即便只是想添加另外一個鏈接點,對切面進行的任何修改都要求你從新編譯整個應用程序。 AspectJ的編譯時編織是靜態AOP實現的一個很好的例子。
諸如Spring AOP之類的動態AOP實現與靜態AOP實現的不一樣之處在於,編織過程是在運行時動態執行的。如何實現此目標取決於實現,可是正如你將看到的那樣,Spring的方法是爲全部建議對象建立代理,並容許根據須要調用建議。動態AOP的缺點是,一般它的性能不如靜態AOP,可是性能卻在穩步提升。動態AOP實現的主要好處是,於是無需修改主應用程序代碼便可輕鬆修改應用程序的整個切面集。
選擇使用靜態AOP仍是動態AOP是一個至關困難的決定。二者都有其自身的優勢,而且你不限於僅使用一種類型。一般,靜態AOP實現的時間更長,而且趨向於具備更多功能豐富的實現,而且具備更多的可用鏈接點。一般,若是性能絕對相當重要,或者你須要在Spring中未實現的AOP功能,則須要使用AspectJ。在大多數其餘狀況下,Spring AOP是理想的選擇。請記住,Spring已經提供了許多基於AOP的解決方案,例如事務管理,所以在滾動本身的框架以前,請檢查其框架功能!與往常同樣,讓應用程序需求來決定您對AOP實施的選擇,若是技術組合更適合本身的應用程序,則不要將本身侷限於一個實施。一般,Spring AOP不如AspectJ複雜,所以它是理想的首選。
Spring的AOP實施能夠看做是兩個邏輯部分。 第一部分是AOP核心,它提供徹底解耦的純編程AOP功能(也稱爲Spring AOP API)。 AOP實現的第二部分是框架服務集,這些框架服務使AOP易於在您的應用程序中使用。 最重要的是,Spring的其餘組件(例如事務管理器和EJB幫助器類)提供了基於AOP的服務,以簡化應用程序的開發。
AOP Alliance(http://aopalliance.sourceforge.net/)是許多開源AOP項目的表明之間的共同努力,旨在爲AOP實現定義標準的接口集。只要適用,Spring都會使用AOP Alliance接口,而不是定義本身的接口。這使您能夠在支持AOP Alliance接口的多個AOP實現中重用某些建議。
先看一個AOP的例子,編寫一個名爲Agent的類來打印「Bond」:
1 package com.apress.prospring5.ch5; 2 public class Agent { 3 public void speak() { 4 System.out.print("Bond"); 5 } 6 }
而後咱們加強該方法,按AOP的屬於來講,就是想該方法添加Advice,在該方法執行前打印「James 」,在該方法指向後打印「!」
1 package com.apress.prospring5.ch5; 2 import org.aopalliance.intercept.MethodInterceptor; 3 import org.aopalliance.intercept.MethodInvocation; 4 5 public class AgentDecorator implements MethodInterceptor { 6 public Object invoke(MethodInvocation invocation) throws Throwable { 7 System.out.print("James "); 8 Object retVal = invocation.proceed(); 9 System.out.println("!"); 10 return retVal; 11 } 12 }
MethodInterceptor 接口是一個標準的 AOP Alliance接口,用於爲方法調用鏈接點實施環繞加強。MethodInvocation對象表示正在建議的方法調用,而且使用此對象,咱們控制什麼時候容許進行方法調用。可以在調用方法以前,以後以及返回以前執行操做。在這段代碼中,將「James 」爲控制檯輸出,經過調用 invocation.proceed() 來調用該方法(指 speak()),而後打印"!"。
下面是一個測試實例,建立目標實例和用代理工廠建立代理進行編織:
1 package com.apress.prospring5.ch5; 2 import org.springframework.aop.framework.ProxyFactory; 3 4 public class AgentAOPDemo { 5 public static void main(String... args) { 6 Agent target = new Agent(); 7 ProxyFactory pf = new ProxyFactory(); 8 pf.addAdvice(new AgentDecorator()); 9 pf.setTarget(target); 10 Agent proxy = (Agent) pf.getProxy(); 11 target.speak(); 12 System.out.println(""); 13 proxy.speak(); 14 } 15 }
使用 ProxyFactory 類建立目標對象的代理,同時編織advice。經過調用addAdvice() 將 AgentDecorator 建議傳遞給 ProxyFactory,並經過調用 setTarget() 來指定編織目標,調用getProxy() 生成代理,這裏調用了原始目標和代理的speak方法。
輸出:
代理的 speak() 調用會致使 AgentDecorator 中的代碼執行,從而輸出所需的"James Bond!"。
Spring AOP的核心架構基於代理。使用ProxyFactory是建立AOP代理的純編程方法,還能夠依靠Spring提供的聲明式 AOP 配置機制(ProxyFactoryBean類,aop命名空間和@AspectJ樣式的註釋)來利用聲明式代理建立。在運行時,Spring分析爲 ApplicationContext 中的bean定義的橫切關注點,並動態生成代理bean(包裝基礎目標bean)。代替直接調用目標bean,調用者被注入代理bean。而後,代理bean分析運行情況(即,鏈接點,切入點或advice),並相應地編織適當的advice。
當要加強的目標對象實現接口時,Spring將使用JDK動態代理建立目標的代理實例。可是,若是建議的目標對象未實現接口(例如,它是一個具體的類),則CGLIB將用於建立代理實例。
Spring AOP中最明顯的最簡化的其中一種是僅支持一種鏈接點類型:方法調用。方法調用聯接點是迄今爲止可用的最有用的聯接點,使用它能夠完成許多使AOP在平常編程中有用的任務。
在Spring AOP中,切面由實現Advisor接口的類的實例表示。Spring提供了便捷的Advisor的實現,能夠在應用程序中重複使用它們。從而消除了必定要本身建立自定義Advisor實施的須要。Advisor 有兩個子接口:PointcutAdvisor 和 IntroductionAdvisor。 PointcutAdvisor 接口繼承了 Advisor 接口並使用使用切入點管理應用於鏈接點的advice。 在Spring中,引介被視爲特殊的加強,經過使用 IntroductionAdvisor 接口,能夠操做,引介所適用的類。
ProxyFactory類控制Spring AOP中的編織和代理建立過程。 在建立代理以前,必須指定建議對象或目標對象。 您能夠經過使用setTarget() 方法來執行此操做。 在內部,ProxyFactory 將代理建立過程委託給DefaultAopProxyFactory的一個實例,該實例又委託給Cglib2AopProxy或JdkDynamicAopProxy。
1 public class ProxyFactory extends ProxyCreatorSupport { 2 …… 3 public Object getProxy() { 4 return createAopProxy().getProxy(); 5 } 6 …… 7 8 } 9 10 11 public class ProxyCreatorSupport extends AdvisedSupport { 12 …… 13 /** 14 * Create a new ProxyCreatorSupport instance. 15 */ 16 public ProxyCreatorSupport() { 17 this.aopProxyFactory = new DefaultAopProxyFactory(); 18 } 19 …… 20 /** 21 * Subclasses should call this to get a new AOP proxy. They should <b>not</b> 22 * create an AOP proxy with {@code this} as an argument. 23 */ 24 protected final synchronized AopProxy createAopProxy() { 25 if (!this.active) { 26 activate(); 27 } 28 return getAopProxyFactory().createAopProxy(this); 29 } 30 …… 31 } 32
DefaultAopProxyFactory.java
1 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { 2 3 @Override 4 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 5 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 6 Class<?> targetClass = config.getTargetClass(); 7 if (targetClass == null) { 8 throw new AopConfigException("TargetSource cannot determine target class: " + 9 "Either an interface or a target is required for proxy creation."); 10 } 11 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { 12 return new JdkDynamicAopProxy(config); 13 } 14 return new ObjenesisCglibAopProxy(config); 15 } 16 else { 17 return new JdkDynamicAopProxy(config); 18 } 19 } 20 21 …… 22 23 }
Spring支持六種 advice,以下表所述: