1. 程式人生 > >對Spring AOP框架實現的結構分析

對Spring AOP框架實現的結構分析

本文的目標:

從實現的角度來認識SpringAOP框架。

觀察的角度:

從外部介面,內部實現,組成部分,執行過程四個方面來認識SpringAOP框架。

本文的風格:

首先列出AOP的基本概念;

其次介紹框架所涉及到的核心元件列表,元件之間的結構關係圖;

然後細化結構圖中的部分;

接下來是一個簡單的sample;

最後是後記部分。

注:

1.本文的原始碼基於Spring2.x。Spring的原始碼也處於演變中,但對基礎程式碼的影響並不大。

2.本文是對Spring IoC容器實現的結構分析的姊妹帖。

正文:

Spring AOP框架涉及的基本概念介紹:

關注點(concern):一個關注點可以是一個特定的問題、概念、或是應用程式的興趣區間--總而言之,應用程式必須達到的一個目標。

核心關注點(core concern):業務功能模組,如:存款模組,取款模組,轉賬模組等,

橫切關注點(crosscutting concern):非功能性的、橫切性模組,如:安全性管理,事務管理,效能監控等。

方面(aspect):一個方面是對一個橫切關注點的模組化,它將那些原本散落在各處的、用於實現這個關注點的程式碼歸整到一處。

連線點(join point):程式執行過程中的一點,如:

欄位訪問:讀、寫例項變數;

方法呼叫:對方法(包括構造方法)的呼叫;

異常丟擲:特定的異常被丟擲。

切入點(pointcut):一組連線點的總稱,用於指定某個增強應該在何時被呼叫。切入點常用正則表示式或別的萬用字元語法來描述,有些AOP實現技術還支援切入點的組合。

增強(advice):在特定連線點執行的動作。很多AOP框架都以攔截器(interceptor)的形式來表現增強--所謂攔截器是這樣的一個

物件:當連線點被呼叫時,它會收到一個回撥訊息。基本的增強有:

前增強(BeforeAdvice):在連線點呼叫之前,首先呼叫增強;

後增強(AfterAdvice):在連線點呼叫之後,再呼叫增強,在AspectJ中,後增強又分為三種:

AfterReturningAdvice:在呼叫成功完成(沒有異常丟擲)之後。

AfterThrowingAdvice:在丟擲某種特定型別(或其子型別)的異常之後。

AfterAdvice:在連線點的任何呼叫之後,不管呼叫是否丟擲異常。

環繞增強(AroundAdvice):這類增強可以完全控制執行流程。除了完成本身的工作之外,它還需要負責主動呼叫連線點,促使真實的操作發生(proceed)-- 這通常是通過呼叫某個特定的方法來完成的。

引介(introduction):為一個現有的Java類或介面新增方法或欄位。這種技術可以用於實現Java中的多繼承,或者給現有物件模型附加新的API。

混入繼承(mixin inheritance):一個“混入類”封裝了一組功能,這組功能可以被"混入"到現有的類當中,並且無須使用傳統的繼承手段。在AOP這裡,混入是通過引介來實現的。在Java語言中,可以通過混入來模擬多繼承。

織入(weaving):將方面整合到完整的執行流程(或完整的類,此時被織入的便是引介中)。

攔截器(initerceptor):很多AOP框架用它來實現欄位和方法的攔截(interception)。隨之而來的就是在連線點(如方法攔截)處掛接一條攔截器鏈(interceptor chain),鏈條上的每個攔截器通常會呼叫下一個攔截器。

AOP代理(AOP proxy):即被增強(advise)的物件引用--也就是說,AOP增強將在其上執行的這樣一個物件引用。

目標物件(target object):位於攔截器鏈末端的物件例項--這個概念只存在於那些使用了攔截機制的框架之中。

注:上述概念描述引自《Expert One-on-One J2EE Development without EJB》中第八章對AOP概念描述部分,更多精彩部分可以參閱本章的完整內容。

上述概念已被Spring AOP框架很好的實現,相關元件:

Advisor 元件,

Advice 元件,

Pointcut 元件,

Advised 元件,

AopProxy 元件,

AopProxyFactory 元件,

圖1.

圖1是對增強、切入點、方面、AOP代理之間依賴關係的全景圖。

增強和切入點組成一個方面,方面資訊與目標物件資訊被組織到Advised中,AopProxyFactory通過Advised中儲存的資訊生成AopProxy

物件,呼叫AopProxy.getProxy()方法即可獲得增強後的物件。

這裡要著重瞭解的是不同的增強子型別,不同的切入點子型別,

對於不同的切入點子型別最重要的兩種子型別:靜態切入點,動態切入點,

靜態切入點:根據部署階段的資訊選擇增強,如“攔截特定類的所有getter方法”;

動態切入點:根據執行時的資訊選擇增強,如“如果某方法的返回值為null,則將其納入某切入點”。

圖2.

圖2是對圖1中Advisor與Pointcut的實現細化,圖中類之間的關係直觀上有點亂,但細看下關係還是相當清晰的,

以Advisor結尾的是方面型別,以Pointcut結尾的是切入點型別,

Advisor與Pointcut的複用關係分兩類:一類是組合複用,另一類是具體繼承複用,

組合複用例子 如:RegexpMethodPointcutAdvisor 與 AbstractRegexpMethodPointcut之間的關係,

NameMatchMethodPointcutAdvisor 與 NameMatchMethodPointcut之間的關係,

具體繼承複用例子 如:StaticMethodMatcherPointcutAdvisor 與 StaticMethodMatcherPointcut 之間的關係,

DynamicMethodMatcherPointcutAdvisor 與 DynamicMethodMatcherPointcut 之間的關係,

圖3.

圖3是對圖1中生成AopProxy物件的實現細化,

AopProxyFactory通過AdvisedSupport提供的資訊生成AopProxy物件,AopProxy物件的生成分兩類方式:一類是動態代理,另一類是位元組碼增強;

需要注意的是,ProxyFactory與ProxyFactoryBean並不是功能實現的必要部分,主要目的為程式設計式使用代理提供便利的API。

下面是一個簡單的sample:

  1. //目標物件介面.
  2. public interface Target {
  3. public String play(int arg);
  4. }
  5. //目標物件實現.
  6. public class TargetImpl implements Target {
  7. public String play(int arg) {
  8. System.out.println("play method....");
  9. return "[Target:]" + arg;
  10. }
  11. }
  12. //前置增強
  13. public class MyBeforeAdvice implements MethodBeforeAdvice {
  14. public void before(Method method, Object[] args, Object target)
  15. throws Throwable {
  16. System.out.println(method.getName());
  17. System.out.println("before method!");
  18. }
  19. }
  20. //後置增強
  21. public class MyAfterAdvice implements AfterReturningAdvice {
  22. public void afterReturning(Object returnValue, Method method,
  23. Object[] args, Object target) throws Throwable {
  24. System.out.println(returnValue + ":after method");
  25. }
  26. }
  27. //切入點實現
  28. public class MyPointcut implements Pointcut {
  29. public ClassFilter getClassFilter() {
  30. return new ClassFilter() {
  31. public boolean matches(Class arg0) {
  32. if (arg0 == TargetImpl.class) {
  33. return true;
  34. }
  35. return false;
  36. }
  37. };
  38. }
  39. public MethodMatcher getMethodMatcher() {
  40. return new MethodMatcher() {
  41. public boolean isRuntime() {
  42. return false;
  43. }
  44. public boolean matches(Method arg0, Class arg1) {
  45. if ("play".equals(arg0.getName())) {
  46. return true;
  47. }
  48. return false;
  49. }
  50. public boolean matches(Method arg0, Class arg1, Object[] arg2) {
  51. System.out.println("aaaaaa");
  52. if ("play".equals(arg0.getName())) {
  53. return true;
  54. }
  55. return false;
  56. }
  57. };
  58. }
  59. }
  60. public class Main {
  61. public static void main(String[] args) {
  62. Target target = new TargetImpl(); //目標物件
  63. Advice beforeAdvice = new MyBeforeAdvice(); //增強
  64. Pointcut pointcut = new MyPointcut(); //切入點
  65. DefaultPointcutAdvisor dda = new DefaultPointcutAdvisor(); //切面
  66. dda.setAdvice(beforeAdvice);
  67. dda.setPointcut(pointcut);
  68. AdvisedSupport advisedSupport = new AdvisedSupport(); //提供基本的程式設計方式,
  69. advisedSupport.addAdvisor(dda);
  70. advisedSupport.addAdvice(new MyAfterAdvice());
  71. advisedSupport.addInterface(Target.class);
  72. advisedSupport.setTarget(target);
  73. AopProxy aopProxy = new DefaultAopProxyFactory().createAopProxy(advisedSupport);
  74. Target proxy = (Target)aopProxy.getProxy();
  75. System.out.println(proxy.play(200));
  76. ProxyFactory proxyFactory = new ProxyFactory(); //提供便利的程式設計方式.
  77. proxyFactory.addAdvisor(dda);
  78. proxyFactory.addInterface(Target.class);
  79. proxyFactory.setTarget(target);
  80. Target proxy2 = (Target)proxyFactory.getProxy();
  81. System.out.println(proxy2.play(201));
  82. ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //提供便利的配置方式
  83. proxyFactoryBean.addAdvisor(dda);
  84. proxyFactoryBean.addInterface(Target.class);
  85. proxyFactoryBean.setTarget(target);
  86. Target proxy3 = (Target)proxyFactoryBean.getObject();
  87. System.out.println(proxy3.play(232));
  88. }
  89. }

注:此處為了簡單忽略了一些概念描述:

1.對引介,混入繼承沒有涉及。

2.對通知鏈的管理,如:同一切入點多個通知物件之間的執行順序問題;

3.上述的描述是Spring AOP框架本身,所提供的使用方式是程式設計式,這種使用方式太過於低階,以至於我們在九成的情況下不會使用到,主流的使用方式是配置化的宣告式使用方式,將AOP與IoC結合起來使用才能發揮出最大威力,ProxyFactoryBean提供了有限的宣告式使用方式,但嚴格來說它仍是程式設計式,因為ProxyFactoryBean是一個FactoryBean,一個FactoryBean的目標就是以程式設計式替換複雜的配置式,而且最重要的是它暴露的API太過低階,配置檔案中bean元素的abstract屬性對配置檔案的長度提供有限的幫助,自動代DefaultAdvisorAutoProxyCreator很好的隱藏了低階的API,DefaultAdvisorAutoProxyCreator是一個BeanPostProcessor,用於完成AOP框架與IoC容器的整合工作,但是這種方式依然沒有解決需要同XXXAdvisor這樣的低階API打交道的問題;

隨著spring2.x引入的xml元素及@Aspect註解的AspectJ描述性風格的出現,使用Spring AOP框架的使用達到完全的宣告式標準,這種風格也使得Spring 事務框架受益,從TransactionProxyFactoryBean類到xml元素、

及@Transactional註解,

原文:http://www.iteye.com/topic/1114645

使得我們只需關注高層描述,而無需涉及低階API。

附上DefaultAdvisorAutoProxyCreator的類結構圖:

總結:

要全面理解AOP相關概念,回答下述問題是必須的。

1。AOP概念產生的背景,AOP所要解決的問題,AOP所涉及的概念,

2。實現一個AOP框架所需要注意的問題是什麼,

3。不同AOP框架實現之間的比較,

4。AOP的一些副作用討論。

注:以上內容來自網友,原文http://developer.51cto.com/art/201109/291414_1.htm