AOP(Aspect Oriented Programming)也就是面向方面編程的技術AOP基於IoC基礎是對OOP的有益補充
AOP將應用系統分為兩部分核心業務邏輯(Core business concerns)及橫向的通用邏輯也就是所謂的方面Crosscutting enterprise concerns例如所有大中型應用都要涉及到的持久化管理(Persistent)事務管理(Transaction Management)安全管理(Security)日志管理(Logging)和調試管理(Debugging)等
AOP正在成為軟件開發的下一個光環使用AOP你可以將處理aspect的代碼注入主程序通常主程序的主要目的並不在於處理這些aspectAOP可以防止代碼混亂
Spring framework是很有前途的AOP技術作為一種非侵略性的輕型的AOP framework你無需使用預編譯器或其他的元標簽便可以在Java程序中使用它這意味著開發團隊裡只需一人要對付AOP framework其他人還是像往常一樣編程
AOP概念
讓我們從定義一些重要的AOP概念開始
— 方面(Aspect)一個關注點的模塊化這個關注點實現可能另外橫切多個對象事務管理是JEE應用中一個很好的橫切關注點例子方面用Spring的Advisor或攔截器實現
— 連接點(Joinpoint)程序執行過程中明確的點如方法的調用或特定的異常被拋出
— 通知(Advice)在特定的連接點AOP框架執行的動作各種類型的通知包括aroundbefore和throws通知通知類型將在下面討論許多AOP框架包括Spring都是以攔截器做通知模型維護一個圍繞連接點的攔截器鏈
— 切入點(Pointcut)指定一個通知將被引發的一系列連接點的集合AOP框架必須允許開發者指定切入點例如使用正則表達式
— 引入(Introduction)添加方法或字段到被通知的類Spring允許引入新的接口到任何被通知的對象例如你可以使用一個引入使任何對象實現IsModified接口來簡化緩存
— 目標對象(Target Object)包含連接點的對象也被稱作被通知或被代理對象
— AOP代理(AOP Proxy)AOP框架創建的對象包含通知在Spring中AOP代理可以是JDK動態代理或CGLIB代理
— 編織(Weaving)組裝方面來創建一個被通知對象這可以在編譯時完成(例如使用AspectJ編譯器)也可以在運行時完成Spring和其他純Java AOP框架一樣在運行時完成織入
各種通知類型包括
— Around通知包圍一個連接點的通知如方法調用這是最強大的通知Aroud通知在方法調用前後完成自定義的行為它們負責選擇繼續執行連接點或通過返回它們自己的返回值或拋出異常來短路執行
— Before通知在一個連接點之前執行的通知但這個通知不能阻止連接點前的執行(除非它拋出一個異常)
— Throws通知在方法拋出異常時執行的通知Spring提供強制類型的Throws通知因此你可以書寫代碼捕獲感興趣的異常(和它的子類)不需要從Throwable或Exception強制類型轉換
— After returning通知在連接點正常完成後執行的通知例如一個方法正常返回沒有拋出異常
Around通知是最通用的通知類型大部分基於攔截的AOP框架(如Nanning和Jboss )只提供Around通知
如同AspectJSpring提供所有類型的通知我們推薦你使用最為合適的通知類型來實現需要的行為例如如果只是需要用一個方法的返回值來更新緩存你最好實現一個after returning通知而不是around通知雖然around通知也能完成同樣的事情使用最合適的通知類型使編程模型變得簡單並能減少潛在錯誤例如你不需要調用在around通知中所需使用的MethodInvocation的proceed()方法因此就調用失敗
切入點的概念是AOP的關鍵它使AOP區別於其他使用攔截的技術切入點使通知獨立於OO的層次選定目標例如提供聲明式事務管理的around通知可以被應用到跨越多個對象的一組方法上 因此切入點構成了AOP的結構要素
下面讓我們實現一個Spring AOP的例子在這個例子中我們將實現一個before advice這意味著advice的代碼在被調用的public方法開始前被執行以下是這個before advice的實現代碼
package comascenttechspringaoptest;
import javalangreflectMethod;
import orgspringframeworkaopMethodBeforeAdvice;
public class TestBeforeAdvice implements MethodBeforeAdvice {
public void before(Method m Object[] args Object target)
throws Throwable {
Systemoutprintln(Hello world! (by
+ thisgetClass()getName()
+ ));
}
}
接口MethodBeforeAdvice只有一個方法before需要實現它定義了advice的實現before方法共用個參數它們提供了相當豐富的信息參數Method m是advice開始後執行的方法方法名稱可以用作判斷是否執行代碼的條件Object[] args是傳給被調用的public方法的參數數組當需要記日志時參數args和被執行方法的名稱都是非常有用的信息你也可以改變傳給m的參數但要小心使用這個功能編寫最初主程序的程序員並不知道主程序可能會和傳入參數的發生沖突Object target是執行方法m對象的引用
在下面的BeanImpl類中每個public方法調用前都會執行advice代碼如下
package comascenttechspringaoptest;
public class BeanImpl implements Bean {
public void theMethod() {
Systemoutprintln(thisgetClass()getName()
+ + new Exception()getStackTrace()[]getMethodName()
+ ()
+ says HELLO!);
}
}
類BeanImpl實現了下面的接口Bean代碼如下
package comascenttechspringaoptest;
public interface Bean {
public void theMethod();
}
雖然不是必須使用接口但面向接口而不是面向實現編程是良好的編程實踐Spring也鼓勵這樣做
pointcut和advice通過配置文件來實現因此接下來你只需編寫主方法的Java代碼代碼如下
package comascenttechspringaoptest;
import orgntextApplicationContext;
import orgntextsupportFileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//Read the configuration file
ApplicationContext ctx
= new FileSystemXmlApplicationContext(springconfigxml);
//Instantiate an object
Bean x = (Bean) ctxgetBean(bean);
//Execute the public method of the bean (the test)
xtheMethod();
}
}
我們從讀入和處理配置文件開始接下來馬上要創建它這個配置文件將作為粘合程序不同部分的膠水讀入和處理配置文件後我們會得到一個創建工廠ctx任何一個Spring管理的對象都必須通過這個工廠來創建對象通過工廠創建後便可正常使用
僅僅用配置文件便可把程序的每一部分組裝起來代碼如下
<?xml version= encoding=UTF?>
<!DOCTYPE beans PUBLIC //SPRING//DTD BEAN//EN org/dtd/springbeansdtd>
<beans>
<!CONFIG>
<bean id=bean class=orgspringframeworkaopframeworkProxyFactoryBean>
<property name=proxyInterfaces>
<value>comascenttechspringaoptestBean</value>
</property>
<property name=target>
<ref local=beanTarget/>
</property>
<property name=interceptorNames>
<list>
<value>theAdvisor</value>
</list>
</property>
</bean>
<!CLASS>
<bean id=beanTarget class=comascenttechspringaoptestBeanImpl/>
<!ADVISOR>
<!Note: An advisor assembles pointcut and advice>
<bean id=theAdvisor class=orgspringframeworkaopsupportRegexpMethod PointcutAdvisor>
<property name=advice>
<ref local=theBeforeAdvice/>
</property>
<property name=pattern>
<value>com\ascenttech\springaop\test\Bean\theMethod</value>
</property>
</bean>
<!ADVICE>
<bean id=theBeforeAdvice class=comascenttechspringaoptestTestBefore Advice/>
</beans>
個bean定義的次序並不重要我們現在有了一個advice一個包含了正則表達式pointcut的advisor一個主程序類和一個配置好的接口通過工廠ctx這個接口返回自己本身實現的一個引用
BeanImpl和TestBeforeAdvice都是直接配置我們用一個惟一的ID創建一個bean元素並指定了一個實現類這就是全部的工作
advisor通過Spring framework提供的一個RegexMethodPointcutAdvisor類來實現我們用advisor的第一個屬性來指定它所需的advicebean第二個屬性則用正則表達式定義了pointcut確保良好的性能和易讀性
最後配置的是bean它可以通過一個工廠來創建bean的定義看起來比實際上要復雜bean是ProxyFactoryBean的一個實現它是Spring framework的一部分這個bean的行為通過以下的個屬性來定義
— 屬性proxyInterface定義了接口類
— 屬性target指向本地配置的一個bean這個bean返回一個接口的實現
— 屬性interceptorNames是惟一允許定義一個值列表的屬性這個列表包含所有需要在beanTarget上執行的advisor注意advisor列表的次序是非常重要的
原文
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26338.html