本文的目的不是要介紹構成模塊化J2EE系統——即Spring框架——的所有重要元素,我們將只把注意力放在Spring所提供的AOP功能上。由于Spring的模塊化設計方法,我們可以只使用該框架的AOP元素,而無(wú)需對構成Spring框架的其他模塊做太多考慮。
在A(yíng)OP方面,Spring提供了什么?
“它的目標不是提供最完善的AOP實(shí)現(雖然Spring AOP非常強大);而是要提供AOP實(shí)現與Spring IoC的緊密集成,以便幫助解決企業(yè)應用中的常見(jiàn)問(wèn)題。”
Spring Framework參考文檔
為了實(shí)現這個(gè)目標,Spring框架目前支持一組AOP概念,從切入點(diǎn)到通知。本文將展示如何使用Spring框架中所實(shí)現的如下AOP概念:
設置場(chǎng)景:一個(gè)簡(jiǎn)單的例子應用程序
“一般而言,Spring并不是預描述的。雖然使用好的實(shí)踐非常容易,但是它避免強制推行一種特定的方法。”
Spring Framework參考文檔
要試用Spring框架的AOP功能,首先我們要創(chuàng )建一個(gè)簡(jiǎn)單的Java應用程序。IbusinessLogic接口和BusinessLogic類(lèi)為Spring框架中的bean提供了簡(jiǎn)易構件塊。雖然該接口對于我們的簡(jiǎn)單應用程序邏輯來(lái)說(shuō)不是必需的,但是它是Spring框架所推薦的良好實(shí)踐。
public interface IBusinessLogic{public void foo();}public class BusinessLogicimplements IBusinessLogic{public void foo(){System.out.println("Inside BusinessLogic.foo()");}}可以編寫(xiě)MainApplication類(lèi),借此練習BusinessLogic bean的公有方法。
import org.springframework.context.ApplicationContext;import org.springframework.context.support.FileSystemXmlApplicationContext;public class MainApplication{public static void main(String [] args){// Read the configuration fileApplicationContext ctx =new FileSystemXmlApplicationContext("springconfig.xml");//Instantiate an objectIBusinessLogic testObject =(IBusinessLogic) ctx.getBean("businesslogicbean");// Execute the public// method of the beantestObject.foo();}}在BusinessLogic類(lèi)及其關(guān)聯(lián)接口中沒(méi)有什么需要注意的。但是,MainApplication類(lèi)初始化BusinessLogic對象的方式很有意思。通過(guò)使用ctx.getBean("businesslogicbean")調用,MainApplication將加載和管理BusinessLogic類(lèi)的bean實(shí)例的任務(wù)轉交給了Spring框架。
允許Spring控制BusinessLogic bean的初始化,這使得Spring運行時(shí)有機會(huì )在bean被返回給應用程序之前執行J2EE系統所需的所有與bean相關(guān)的管理任務(wù)。然后Spring運行時(shí)配置可以決定對bean應用哪些任務(wù)和模塊。該配置信息由一個(gè)XML文件提供,類(lèi)似于下面所示的:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd"><beans><!-- Bean configuration --><bean id="businesslogicbean"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>IBusinessLogic</value></property><property name="target"><ref local="beanTarget"/></property></bean><!-- Bean Classes --><bean id="beanTarget"class="BusinessLogic"/></beans>
該配置文件,即springconfig.xml,指定要加載一個(gè)接口與IbusinessLogic相匹配的bean。該bean隨后被關(guān)聯(lián)到BusinessLogic實(shí)現類(lèi)??雌饋?lái)好像是費了很大力氣只為了加載一個(gè)簡(jiǎn)單的bean并調用一個(gè)方法,但是您要知道,這個(gè)配置文件只是使Spring框架可以透明地對應用程序應用其組件的眾多特性的一個(gè)體現。
圖1顯示了基本的順序圖:MainApplication原樣執行,沒(méi)有應用方面。

請查看本文末尾處的參考資料,獲取這個(gè)簡(jiǎn)單Spring應用程序的源代碼。
應用方法跟蹤(Method Tracing)方面
可能最基本的方面就是方法跟蹤方面了。這可能是您找得到的最簡(jiǎn)單的方面了,因此它是研究新的AOP實(shí)現的一個(gè)很好的起點(diǎn)。
方法跟蹤方面在一個(gè)目標應用程序內捕獲對所跟蹤的方法的調用以及方法的返回值,并以某種方式顯示這種信息。在A(yíng)OP中,通知的before和after類(lèi)型用于捕獲這些類(lèi)型的聯(lián)結點(diǎn),因為這兩種通知可以在方法調用聯(lián)結點(diǎn)之前或之后觸發(fā)。使用Spring框架,方法跟蹤方面的before通知是在TracingBeforeAdvice類(lèi)中聲明的。
import java.lang.reflect.Method;import org.springframework.aop. MethodBeforeAdvice;public class TracingBeforeAdviceimplements MethodBeforeAdvice{public void before(Method m,Object[] args,Object target)throws Throwable{System.out.println("Hello world! (by " +this.getClass().getName() +")");}}類(lèi)似地,after通知可以在TracingAfterAdvice類(lèi)中聲明。
import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class TracingAfterAdviceimplements AfterReturningAdvice{public void afterReturning(Object object,Method m,Object[] args,Object target)throws Throwable{System.out.println("Hello world! (by " +this.getClass().getName() +")");}}這兩個(gè)類(lèi)都通過(guò)實(shí)現Spring框架的適當通知接口而表示了特定的通知。每種類(lèi)型的通知都指定實(shí)現before(..)或afterReturning(..)方法,以便使Spring運行時(shí)可以告訴通知適當的聯(lián)結點(diǎn)會(huì )在何時(shí)出現。值得注意的是,TracingAfterAdvice實(shí)際上是從AfterReturningAdvice擴展而來(lái)的,表示只有在聯(lián)結點(diǎn)在無(wú)異常的情況下獲得返回值時(shí)才運行通知。
為了將通知與應用程序中的適當聯(lián)結點(diǎn)關(guān)聯(lián)起來(lái),必須對springconfig.xml進(jìn)行一些修改。
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd"><beans><!-- Bean configuration --><bean id="businesslogicbean"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>IBusinessLogic</value></property><property name="target"><ref local="beanTarget"/></property><property name="interceptorNames"><list><value>theTracingBeforeAdvisor</value><value>theTracingAfterAdvisor</value></list></property></bean><!-- Bean Classes --><bean id="beanTarget"class="BusinessLogic"/><!-- Advisor pointcut definition for before advice --><bean id="theTracingBeforeAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="advice"><ref local="theTracingBeforeAdvice"/></property><property name="pattern"><value>.*</value></property></bean><!-- Advisor pointcut definition for after advice --><bean id="theTracingAfterAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="advice"><ref local="theTracingAfterAdvice"/></property><property name="pattern"><value>.*</value></property></bean<<!-- Advice classes --><bean id="theTracingBeforeAdvice"class="TracingBeforeAdvice"/><bean id="theTracingAfterAdvice"class="TracingAfterAdvice"/></beans>
theTracingBeforeAdvisor和theTracingAfterAdvisor advisor被添加到前面所聲明的businesslogicbean。每個(gè)advisor都可能截獲所有bean所關(guān)聯(lián)到的聯(lián)結點(diǎn)。Advisor本身就是bean,而它唯一的作用就是將切入點(diǎn)定義與通知bean關(guān)聯(lián)起來(lái)。本例中的切入點(diǎn)定義是在靜態(tài)對象層次結構中指定相關(guān)聯(lián)結點(diǎn)的正則表達式。
因為本例中使用了org.springframework.aop.support.RegexpMethodPointcutAdvisor切入點(diǎn)advisor,切入點(diǎn)邏輯是使用正則表達式指定的。正則表達式用于識別公有接口對IbusinessLogici接口的聯(lián)結點(diǎn)。下面是一些可以用來(lái)指定IBusinessLogic接口上的不同聯(lián)結點(diǎn)集合的正則表達式例子:
springconfig.xml文件中最后的bean聲明指定實(shí)現通知bean的類(lèi)。
既然已經(jīng)指定了跟蹤方面的正確配置,那么下一次執行MainApplication時(shí),這些方面就會(huì )在初始化過(guò)程中被編織進(jìn)去,而B(niǎo)usinessLogic bean中的所有方法都將被跟蹤,如圖2所示。

圖2. 方法跟蹤方面應用到BusinessLogic bean之后的順序圖(單擊圖像查看大圖)
方法跟蹤方面和例子應用程序的源代碼可在本文末尾的參考資料小節進(jìn)行下載。
方面的重用
可以對方法跟蹤方面進(jìn)行擴展,提供一個(gè)稍微復雜的記錄(Logging)方面。記錄方面提供了一個(gè)很不錯的重用例子,因為記錄方面所需的許多特性都已經(jīng)包含在方法跟蹤方面中了。
在本例中,記錄方面擴展了方法跟蹤方面,以便顯示附加的與(在應用程序的執行過(guò)程中)所引發(fā)的異常有關(guān)的信息。
要完全使用記錄方面,需要對應用程序做一些更改。BusinessLogicException異常類(lèi)提供了一個(gè)可以由IBusinessLogicInterface接口和BusinessLogic實(shí)現類(lèi)新增的void bar()方法引發(fā)的異常。
public class BusinessLogicExceptionextends Exception{}public interface IBusinessLogic{public void foo();public void bar()throws BusinessLogicException;}public class BusinessLogicimplements IBusinessLogic{public void foo(){System.out.println("Inside BusinessLogic.foo()");}public void bar()throws BusinessLogicException{System.out.println("Inside BusinessLogic.bar()");throw new BusinessLogicException();}}MainApplication類(lèi)現在將對void bar()方法進(jìn)行一次額外的調用,并處理選中的、可能由該方法引發(fā)的異常。
import org.springframeworkcontext.ApplicationContext;import org.springframework.context.support.FileSystemXmlApplicationContext;public class MainApplication{public static void main(String [] args){// Read the configuration fileApplicationContext ctx =new FileSystemXmlApplicationContext("springconfig.xml");//Instantiate an objectIBusinessLogic testObject =(IBusinessLogic) ctx.getBean("businesslogicbean");//Execute the public methods of the beantestObject.foo();try{testObject.bar();}catch(BusinessLogicException ble){System.out.println("Caught BusinessLogicException");}}}來(lái)自方法跟蹤方面的TracingBeforeAdvice和TracingAfterAdvice通知可以整體重用。LoggingThrowsAdvice類(lèi)為新的異常記錄提供了通知。
import org.springframework.aop.ThrowsAdvice;import java.lang.reflect.Method;public class LoggingThrowsAdviceimplements ThrowsAdvice{public void afterThrowing(Method method,Object[] args,Object target,Throwable subclass){System.out.println("Logging that a " +subclass +"Exception was thrown.");}}應用記錄方面的最后一步是修改springconfig.xml配置文件,使其包含新添加的LoggingThrowsAdvice通知。
圖3顯示了運行MainApplication并使用Spring框架應用了記錄方面的UML順序圖。

圖3. 記錄方面應用到BusinessLogic bean之后的順序圖(單擊圖像查看大圖)
此處的記錄方面清楚地說(shuō)明了如何重用現有方面以及如何在Spring框架中使用通知的throws形式。通過(guò)為before和after通知聲明新的通知來(lái)重寫(xiě)現有的方法跟蹤方面實(shí)現,可以實(shí)現更復雜的記錄方面,記錄到更復雜的記錄框架,比如LOG4J。關(guān)于記錄方面和例子應用程序的源代碼,請參見(jiàn)本文末尾的參考資料小節。
結束語(yǔ)
本文展示了使用Spring框架中的基本AOP結構所應用的一些簡(jiǎn)單方面。在本系列的下一篇文章中,我們將介紹一些更實(shí)用的方面,探討方面的生命周期,使用Spring框架的around通知,并使用Spring來(lái)應用AOP模式。
參考資料
原文出處:An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 1http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html

| 作者簡(jiǎn)介 | |
| Russell Miles是General Dynamics UK公司的一名軟件工程師,他負責Java和分布式系統,但是他目前主要的興趣在面向方面領(lǐng)域,尤其是AspectJ。 | |
聯(lián)系客服