Spring AOP四種實現方式



二、AOP相關概念

(1)AOP是什麼?AOP與攔截器的區別?

太抽象的不說,如果你知道Struts2的攔截器,攔截器就是應用的AOP的思想,它用於攔截Action以進行一些預處理或結果處理。spring的AOP是一種更通用的模式,可以攔截Spring管理的Bean,功能更強大,適用範圍也更廣,它是通過動態代理與反射機制實現的。(更詳細的解釋可參看博客 http://blog.csdn.NET/zhangliangzi/article/details/51648032 )

(2)使用AOP需要的一些概念。

1.通知(Advice)

通知定義了在切入點代碼執行時間點附近需要做的工作。

Spring支持五種類型的通知:

Before(前)  org.apringframework.aop.MethodBeforeAdvice
After-returning(返回後) org.springframework.aop.AfterReturningAdvice
After-throwing(拋出後) org.springframework.aop.ThrowsAdvice
Arround(周圍) org.aopaliance.intercept.MethodInterceptor
Introduction(引入) org.springframework.aop.IntroductionInterceptor

2.連接點(Joinpoint)

程序能夠應用通知的一個“時機”,這些“時機”就是連接點,例如方法調用時、異常拋出時、方法返回後等等。

3.切入點(Pointcut)

通知定義了切面要發生的“故事”,連接點定義了“故事”發生的時機,那麼切入點就定義了“故事”發生的地點,例如某個類或方法的名稱,Spring中允許我們方便的用正則表達式來指定。

4.切面(Aspect)

通知、連接點、切入點共同組成了切面:時間、地點和要發生的“故事”。

5.引入(Introduction)

引入允許我們向現有的類添加新的方法和屬性(Spring提供了一個方法注入的功能)。

6.目標(Target)

即被通知的對象,如果沒有AOP,那麼通知的邏輯就要寫在目標對象中,有了AOP之後它可以只關注自己要做的事,解耦合!

7.代理(proxy)

應用通知的對象,詳細內容參見設計模式裏面的動態代理模式。

8.織入(Weaving)

把切面應用到目標對象來創建新的代理對象的過程,織入一般發生在如下幾個時機:

(1)編譯時:當一個類文件被編譯時進行織入,這需要特殊的編譯器纔可以做的到,例如AspectJ的織入編譯器;

(2)類加載時:使用特殊的ClassLoader在目標類被加載到程序之前增強類的字節代碼;

(3)運行時:切面在運行的某個時刻被織入,SpringAOP就是以這種方式織入切面的,原理應該是使用了JDK的動態代理技術。


三、使用AOP的幾種方式

1.經典的基於代理的AOP

2.@AspectJ註解驅動的切面

3.純POJO切面

4.注入式AspectJ切面


四、Demo詳解

在講Demo之前,先把項目結構貼一下,我用的的一般的Java Project+Maven進行測試,用Web Project的小區別一會會說到。有一點很重要,jar依賴必須導入正確,我在測試過程中,很多bug都是因爲依賴問題引起的,這裏也貼一下。

包結構:


pom.xml:

[html] view plain copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.   
  5.     <groupId>com.springAOP</groupId>  
  6.     <artifactId>springAOP</artifactId>  
  7.     <version>0.0.1-SNAPSHOT</version>  
  8.     <packaging>jar</packaging>  
  9.   
  10.     <name>springAOP</name>  
  11.     <url>http://maven.apache.org</url>  
  12.   
  13.     <properties>  
  14.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  15.         <org.springframework.version>3.0.5.RELEASE</org.springframework.version>  
  16.     </properties>  
  17.   
  18.   
  19.     <dependencies>  
  20.   
  21.         <dependency>  
  22.             <groupId>junit</groupId>  
  23.             <artifactId>junit</artifactId>  
  24.             <version>3.8.1</version>  
  25.             <scope>test</scope>  
  26.         </dependency>  
  27.   
  28.         <!-- Spring -->  
  29.         <dependency>  
  30.             <groupId>org.springframework</groupId>  
  31.             <artifactId>spring-context</artifactId>  
  32.             <version>${org.springframework.version}</version>  
  33.         </dependency>  
  34.   
  35.         <!-- Spring AOP + AspectJ -->  
  36.         <dependency>  
  37.             <groupId>org.springframework</groupId>  
  38.             <artifactId>spring-aop</artifactId>  
  39.             <version>${org.springframework.version}</version>  
  40.         </dependency>  
  41.           
  42.         <dependency>  
  43.             <groupId>org.aspectj</groupId>  
  44.             <artifactId>aspectjrt</artifactId>  
  45.             <version>1.8.9</version>  
  46.         </dependency>  
  47.           
  48.         <dependency>  
  49.             <groupId>org.aspectj</groupId>  
  50.             <artifactId>aspectjweaver</artifactId>  
  51.             <version>1.8.9</version>  
  52.         </dependency>  
  53.       
  54.     </dependencies>  
  55. </project>  

下面開始正式的講解:

1、經典的基於代理的AOP實現,以一個睡覺的例子實現。

(1)可睡覺的接口,任何可以睡覺的人或機器都可以實現它。

[java] view plain copy
  1. public interface Sleepable {  
  2.     public void sleep();  
  3. }  
(2)接口實現類,“Me”可以睡覺,“Me”就實現可以睡覺的接口。

[java] view plain copy
  1. public class Me implements Sleepable{  
  2.     public void sleep() {  
  3.         System.out.println("\n睡覺!不休息哪裏有力氣學習!\n");  
  4.     }  
  5. }  
3)Me關注於睡覺的邏輯,但是睡覺需要其他功能輔助,比如睡前脫衣服,起牀脫衣服,這裏開始就需要AOP替“Me”完成!解耦!首先需要一個SleepHelper類。因爲一個是切入點前執行、一個是切入點之後執行,所以實現對應接口。

[java] view plain copy
  1. public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {  
  2.   
  3.     public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {  
  4.         System.out.println("睡覺前要脫衣服!");  
  5.     }  
  6.   
  7.     public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {  
  8.         System.out.println("起牀後要穿衣服!");  
  9.     }  
  10.   
  11. }  
(4)最關鍵的來了,Spring核心配置文件application.xml配置AOP。

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3. <span style="white-space:pre">    </span>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. <span style="white-space:pre">    </span>xmlns:aop="http://www.springframework.org/schema/aop"  
  5. <span style="white-space:pre">    </span>xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6. <span style="white-space:pre">    </span>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop  
  8. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
  9.      
  10.    <!-- 定義被代理者 -->  
  11.    <bean id="me" class="com.springAOP.bean.Me"></bean>  
  12.      
  13.    <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  14.    <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  15.      
  16.    <!-- 定義切入點位置 -->  
  17.    <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">  
  18.         <property name="pattern" value=".*sleep"></property>  
  19.    </bean>  
  20.      
  21.    <!-- 使切入點與通知相關聯,完成切面配置 -->  
  22.    <bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">  
  23.         <property name="advice" ref="sleepHelper"></property>         
  24.         <property name="pointcut" ref="sleepPointcut"></property>  
  25.    </bean>  
  26.      
  27.    <!-- 設置代理 -->  
  28.    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  29.         <!-- 代理的對象,有睡覺能力 -->  
  30.         <property name="target" ref="me"></property>  
  31.         <!-- 使用切面 -->  
  32.         <property name="interceptorNames" value="sleepHelperAdvisor"></property>  
  33.         <!-- 代理接口,睡覺接口 -->  
  34.         <property name="proxyInterfaces" value="com.springAOP.bean.Sleepable"></property>   
  35.    </bean>  
  36.       
  37. </beans>  
其中:

<beans>是Spring的配置標籤,beans裏面幾個重要的屬性:

xmlns:

是默認的xml文檔解析格式,即spring的beans。地址是http://www.springframework.org/schema/beans;通過設置這個屬性,所有在beans裏面聲明的屬性,可以直接通過<>來使用,比如<bean>等等。一個XML文件,只能聲明一個默認的語義解析的規範。例如上面的xml中就只有beans一個是默認的,其他的都需要通過特定的標籤來使用,比如aop,它自己有很多的屬性,如果要使用,前面就必須加上aop:xxx纔可以。類似的,如果默認的xmlns配置的是aop相關的語義解析規範,那麼在xml中就可以直接寫config這種標籤了。

xmlns:xsi:

是xml需要遵守的規範,通過URL可以看到,是w3的統一規範,後面通過xsi:schemaLocation來定位所有的解析文件。

xmlns:aop:

這個是重點,是我們這裏需要使用到的一些語義規範,與面向切面AOP相關。

xmlns:tx:

Spring中與事務相關的配置內容。

(5)測試類,Test,其中,通過AOP代理的方式執行Me的sleep()方法,會把執行前、執行後的操作執行,實現了AOP的效果!

[java] view plain copy
  1. public class Test {  
  2.     public static void main(String[] args){  
  3.         @SuppressWarnings("resource")  
  4.         //如果是web項目,則使用註釋的代碼加載配置文件,這裏是一般的Java項目,所以使用下面的方式  
  5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
  6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
  7.         Sleepable me = (Sleepable)appCtx.getBean("proxy");  
  8.         me.sleep();  
  9.     }  
  10. }  
執行結果:



(6)通過org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator簡化配置。

將配置文件中設置代理的代碼去掉,加上:

[html] view plain copy
  1. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>  
然後,在Test中,直接獲取me對象,執行sleep方法,就可以實現同樣的功能!

通過自動匹配,切面會自動匹配符合切入點的bean,會被自動代理,實現功能!

2、更簡單的方式,通過AspectJ提供的註解實現AOP。

(1)同樣的例子,修改後的SleepHelper:

[java] view plain copy
  1. @Aspect  
  2. public class SleepHelper{  
  3.   
  4.     public SleepHelper(){  
  5.           
  6.     }  
  7.       
  8.     @Pointcut("execution(* *.sleep())")  
  9.     public void sleeppoint(){}  
  10.       
  11.     @Before("sleeppoint()")  
  12.     public void beforeSleep(){  
  13.         System.out.println("睡覺前要脫衣服!");  
  14.     }  
  15.       
  16.     @AfterReturning("sleeppoint()")  
  17.     public void afterSleep(){  
  18.         System.out.println("睡醒了要穿衣服!");  
  19.     }  
  20.       
  21. }  

(2)在方法中,可以加上JoinPoint參數以進行相關操作,如:

[java] view plain copy
  1. //當拋出異常時被調用  
  2.     public void doThrowing(JoinPoint point, Throwable ex)  
  3.     {  
  4.         System.out.println("doThrowing::method "  
  5.                 + point.getTarget().getClass().getName() + "."  
  6.                 + point.getSignature().getName() + " throw exception");  
  7.         System.out.println(ex.getMessage());  
  8.     }  

(3)然後修改配置爲:

[html] view plain copy
  1.        <aop:aspectj-autoproxy />  
  2. <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  3. <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  4. <!-- 定義被代理者 -->  
  5. <bean id="me" class="com.springAOP.bean.Me"></bean>  

(4)最後測試,一樣的結果!

[java] view plain copy
  1. public class Test {  
  2.     public static void main(String[] args){  
  3.         @SuppressWarnings("resource")  
  4.         //如果是web項目,則使用註釋的代碼加載配置文件,這裏是一般的Java項目,所以使用下面的方式  
  5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
  6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
  7.         Sleepable me = (Sleepable)appCtx.getBean("me");  
  8.         me.sleep();  
  9.     }  
  10. }  

3、使用Spring來定義純粹的POJO切面(名字很繞口,其實就是純粹通過<aop:fonfig>標籤配置,也是一種比較簡單的方式)。

(1)修改後的SleepHelper類,很正常的類,所以這種方式的優點就是在代碼中不體現任何AOP相關配置,純粹使用xml配置。

[java] view plain copy
  1. public class SleepHelper{  
  2.   
  3.     public void beforeSleep(){  
  4.         System.out.println("睡覺前要脫衣服!");  
  5.     }  
  6.       
  7.     public void afterSleep(){  
  8.         System.out.println("睡醒了要穿衣服!");  
  9.     }  
  10.      
  11. }  
(2)配置文件:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  6.     http://www.springframework.org/schema/aop  
  7.     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
  8.   
  9.     <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  10.     <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  11.     <!-- 定義被代理者 -->  
  12.     <bean id="me" class="com.springAOP.bean.Me"></bean>  
  13.   
  14.     <aop:config>  
  15.         <aop:aspect ref="sleepHelper">  
  16.             <aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))" />  
  17.             <aop:after method="afterSleep" pointcut="execution(* *.sleep(..))" />  
  18.         </aop:aspect>  
  19.     </aop:config>  
  20.   
  21. </beans>  

(3)配置的另一種寫法

[html] view plain copy
  1. <aop:config>  
  2.     <aop:aspect ref="sleepHelper">  
  3.            <aop:pointcut id="sleepHelpers" expression="execution(* *.sleep(..))" />  
  4.            <aop:before pointcut-ref="sleepHelpers" method="beforeSleep" />  
  5.            <aop:after pointcut-ref="sleepHelpers" method="afterSleep" />            
  6.        </aop:aspect>  
  7. </aop:config>  


五、AOP實現原理

學東西還是要深入進去的,推薦一篇網評還不錯的博文,http://blog.csdn.net/moreevan/article/details/11977115/

二、AOP相關概念

(1)AOP是什麼?AOP與攔截器的區別?

太抽象的不說,如果你知道Struts2的攔截器,攔截器就是應用的AOP的思想,它用於攔截Action以進行一些預處理或結果處理。spring的AOP是一種更通用的模式,可以攔截Spring管理的Bean,功能更強大,適用範圍也更廣,它是通過動態代理與反射機制實現的。(更詳細的解釋可參看博客 http://blog.csdn.NET/zhangliangzi/article/details/51648032 )

(2)使用AOP需要的一些概念。

1.通知(Advice)

通知定義了在切入點代碼執行時間點附近需要做的工作。

Spring支持五種類型的通知:

Before(前)  org.apringframework.aop.MethodBeforeAdvice
After-returning(返回後) org.springframework.aop.AfterReturningAdvice
After-throwing(拋出後) org.springframework.aop.ThrowsAdvice
Arround(周圍) org.aopaliance.intercept.MethodInterceptor
Introduction(引入) org.springframework.aop.IntroductionInterceptor

2.連接點(Joinpoint)

程序能夠應用通知的一個“時機”,這些“時機”就是連接點,例如方法調用時、異常拋出時、方法返回後等等。

3.切入點(Pointcut)

通知定義了切面要發生的“故事”,連接點定義了“故事”發生的時機,那麼切入點就定義了“故事”發生的地點,例如某個類或方法的名稱,Spring中允許我們方便的用正則表達式來指定。

4.切面(Aspect)

通知、連接點、切入點共同組成了切面:時間、地點和要發生的“故事”。

5.引入(Introduction)

引入允許我們向現有的類添加新的方法和屬性(Spring提供了一個方法注入的功能)。

6.目標(Target)

即被通知的對象,如果沒有AOP,那麼通知的邏輯就要寫在目標對象中,有了AOP之後它可以只關注自己要做的事,解耦合!

7.代理(proxy)

應用通知的對象,詳細內容參見設計模式裏面的動態代理模式。

8.織入(Weaving)

把切面應用到目標對象來創建新的代理對象的過程,織入一般發生在如下幾個時機:

(1)編譯時:當一個類文件被編譯時進行織入,這需要特殊的編譯器纔可以做的到,例如AspectJ的織入編譯器;

(2)類加載時:使用特殊的ClassLoader在目標類被加載到程序之前增強類的字節代碼;

(3)運行時:切面在運行的某個時刻被織入,SpringAOP就是以這種方式織入切面的,原理應該是使用了JDK的動態代理技術。


三、使用AOP的幾種方式

1.經典的基於代理的AOP

2.@AspectJ註解驅動的切面

3.純POJO切面

4.注入式AspectJ切面


四、Demo詳解

在講Demo之前,先把項目結構貼一下,我用的的一般的Java Project+Maven進行測試,用Web Project的小區別一會會說到。有一點很重要,jar依賴必須導入正確,我在測試過程中,很多bug都是因爲依賴問題引起的,這裏也貼一下。

包結構:


pom.xml:

[html] view plain copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.   
  5.     <groupId>com.springAOP</groupId>  
  6.     <artifactId>springAOP</artifactId>  
  7.     <version>0.0.1-SNAPSHOT</version>  
  8.     <packaging>jar</packaging>  
  9.   
  10.     <name>springAOP</name>  
  11.     <url>http://maven.apache.org</url>  
  12.   
  13.     <properties>  
  14.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  15.         <org.springframework.version>3.0.5.RELEASE</org.springframework.version>  
  16.     </properties>  
  17.   
  18.   
  19.     <dependencies>  
  20.   
  21.         <dependency>  
  22.             <groupId>junit</groupId>  
  23.             <artifactId>junit</artifactId>  
  24.             <version>3.8.1</version>  
  25.             <scope>test</scope>  
  26.         </dependency>  
  27.   
  28.         <!-- Spring -->  
  29.         <dependency>  
  30.             <groupId>org.springframework</groupId>  
  31.             <artifactId>spring-context</artifactId>  
  32.             <version>${org.springframework.version}</version>  
  33.         </dependency>  
  34.   
  35.         <!-- Spring AOP + AspectJ -->  
  36.         <dependency>  
  37.             <groupId>org.springframework</groupId>  
  38.             <artifactId>spring-aop</artifactId>  
  39.             <version>${org.springframework.version}</version>  
  40.         </dependency>  
  41.           
  42.         <dependency>  
  43.             <groupId>org.aspectj</groupId>  
  44.             <artifactId>aspectjrt</artifactId>  
  45.             <version>1.8.9</version>  
  46.         </dependency>  
  47.           
  48.         <dependency>  
  49.             <groupId>org.aspectj</groupId>  
  50.             <artifactId>aspectjweaver</artifactId>  
  51.             <version>1.8.9</version>  
  52.         </dependency>  
  53.       
  54.     </dependencies>  
  55. </project>  

下面開始正式的講解:

1、經典的基於代理的AOP實現,以一個睡覺的例子實現。

(1)可睡覺的接口,任何可以睡覺的人或機器都可以實現它。

[java] view plain copy
  1. public interface Sleepable {  
  2.     public void sleep();  
  3. }  
(2)接口實現類,“Me”可以睡覺,“Me”就實現可以睡覺的接口。

[java] view plain copy
  1. public class Me implements Sleepable{  
  2.     public void sleep() {  
  3.         System.out.println("\n睡覺!不休息哪裏有力氣學習!\n");  
  4.     }  
  5. }  
3)Me關注於睡覺的邏輯,但是睡覺需要其他功能輔助,比如睡前脫衣服,起牀脫衣服,這裏開始就需要AOP替“Me”完成!解耦!首先需要一個SleepHelper類。因爲一個是切入點前執行、一個是切入點之後執行,所以實現對應接口。

[java] view plain copy
  1. public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {  
  2.   
  3.     public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {  
  4.         System.out.println("睡覺前要脫衣服!");  
  5.     }  
  6.   
  7.     public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {  
  8.         System.out.println("起牀後要穿衣服!");  
  9.     }  
  10.   
  11. }  
(4)最關鍵的來了,Spring核心配置文件application.xml配置AOP。

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3. <span style="white-space:pre">    </span>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. <span style="white-space:pre">    </span>xmlns:aop="http://www.springframework.org/schema/aop"  
  5. <span style="white-space:pre">    </span>xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6. <span style="white-space:pre">    </span>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop  
  8. <span style="white-space:pre">    </span>http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
  9.      
  10.    <!-- 定義被代理者 -->  
  11.    <bean id="me" class="com.springAOP.bean.Me"></bean>  
  12.      
  13.    <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  14.    <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  15.      
  16.    <!-- 定義切入點位置 -->  
  17.    <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">  
  18.         <property name="pattern" value=".*sleep"></property>  
  19.    </bean>  
  20.      
  21.    <!-- 使切入點與通知相關聯,完成切面配置 -->  
  22.    <bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">  
  23.         <property name="advice" ref="sleepHelper"></property>         
  24.         <property name="pointcut" ref="sleepPointcut"></property>  
  25.    </bean>  
  26.      
  27.    <!-- 設置代理 -->  
  28.    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  29.         <!-- 代理的對象,有睡覺能力 -->  
  30.         <property name="target" ref="me"></property>  
  31.         <!-- 使用切面 -->  
  32.         <property name="interceptorNames" value="sleepHelperAdvisor"></property>  
  33.         <!-- 代理接口,睡覺接口 -->  
  34.         <property name="proxyInterfaces" value="com.springAOP.bean.Sleepable"></property>   
  35.    </bean>  
  36.       
  37. </beans>  
其中:

<beans>是Spring的配置標籤,beans裏面幾個重要的屬性:

xmlns:

是默認的xml文檔解析格式,即spring的beans。地址是http://www.springframework.org/schema/beans;通過設置這個屬性,所有在beans裏面聲明的屬性,可以直接通過<>來使用,比如<bean>等等。一個XML文件,只能聲明一個默認的語義解析的規範。例如上面的xml中就只有beans一個是默認的,其他的都需要通過特定的標籤來使用,比如aop,它自己有很多的屬性,如果要使用,前面就必須加上aop:xxx纔可以。類似的,如果默認的xmlns配置的是aop相關的語義解析規範,那麼在xml中就可以直接寫config這種標籤了。

xmlns:xsi:

是xml需要遵守的規範,通過URL可以看到,是w3的統一規範,後面通過xsi:schemaLocation來定位所有的解析文件。

xmlns:aop:

這個是重點,是我們這裏需要使用到的一些語義規範,與面向切面AOP相關。

xmlns:tx:

Spring中與事務相關的配置內容。

(5)測試類,Test,其中,通過AOP代理的方式執行Me的sleep()方法,會把執行前、執行後的操作執行,實現了AOP的效果!

[java] view plain copy
  1. public class Test {  
  2.     public static void main(String[] args){  
  3.         @SuppressWarnings("resource")  
  4.         //如果是web項目,則使用註釋的代碼加載配置文件,這裏是一般的Java項目,所以使用下面的方式  
  5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
  6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
  7.         Sleepable me = (Sleepable)appCtx.getBean("proxy");  
  8.         me.sleep();  
  9.     }  
  10. }  
執行結果:



(6)通過org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator簡化配置。

將配置文件中設置代理的代碼去掉,加上:

[html] view plain copy
  1. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>  
然後,在Test中,直接獲取me對象,執行sleep方法,就可以實現同樣的功能!

通過自動匹配,切面會自動匹配符合切入點的bean,會被自動代理,實現功能!

2、更簡單的方式,通過AspectJ提供的註解實現AOP。

(1)同樣的例子,修改後的SleepHelper:

[java] view plain copy
  1. @Aspect  
  2. public class SleepHelper{  
  3.   
  4.     public SleepHelper(){  
  5.           
  6.     }  
  7.       
  8.     @Pointcut("execution(* *.sleep())")  
  9.     public void sleeppoint(){}  
  10.       
  11.     @Before("sleeppoint()")  
  12.     public void beforeSleep(){  
  13.         System.out.println("睡覺前要脫衣服!");  
  14.     }  
  15.       
  16.     @AfterReturning("sleeppoint()")  
  17.     public void afterSleep(){  
  18.         System.out.println("睡醒了要穿衣服!");  
  19.     }  
  20.       
  21. }  

(2)在方法中,可以加上JoinPoint參數以進行相關操作,如:

[java] view plain copy
  1. //當拋出異常時被調用  
  2.     public void doThrowing(JoinPoint point, Throwable ex)  
  3.     {  
  4.         System.out.println("doThrowing::method "  
  5.                 + point.getTarget().getClass().getName() + "."  
  6.                 + point.getSignature().getName() + " throw exception");  
  7.         System.out.println(ex.getMessage());  
  8.     }  

(3)然後修改配置爲:

[html] view plain copy
  1.        <aop:aspectj-autoproxy />  
  2. <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  3. <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  4. <!-- 定義被代理者 -->  
  5. <bean id="me" class="com.springAOP.bean.Me"></bean>  

(4)最後測試,一樣的結果!

[java] view plain copy
  1. public class Test {  
  2.     public static void main(String[] args){  
  3.         @SuppressWarnings("resource")  
  4.         //如果是web項目,則使用註釋的代碼加載配置文件,這裏是一般的Java項目,所以使用下面的方式  
  5.         //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
  6.         ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
  7.         Sleepable me = (Sleepable)appCtx.getBean("me");  
  8.         me.sleep();  
  9.     }  
  10. }  

3、使用Spring來定義純粹的POJO切面(名字很繞口,其實就是純粹通過<aop:fonfig>標籤配置,也是一種比較簡單的方式)。

(1)修改後的SleepHelper類,很正常的類,所以這種方式的優點就是在代碼中不體現任何AOP相關配置,純粹使用xml配置。

[java] view plain copy
  1. public class SleepHelper{  
  2.   
  3.     public void beforeSleep(){  
  4.         System.out.println("睡覺前要脫衣服!");  
  5.     }  
  6.       
  7.     public void afterSleep(){  
  8.         System.out.println("睡醒了要穿衣服!");  
  9.     }  
  10.      
  11. }  
(2)配置文件:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  6.     http://www.springframework.org/schema/aop  
  7.     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
  8.   
  9.     <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->  
  10.     <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
  11.     <!-- 定義被代理者 -->  
  12.     <bean id="me" class="com.springAOP.bean.Me"></bean>  
  13.   
  14.     <aop:config>  
  15.         <aop:aspect ref="sleepHelper">  
  16.             <aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))" />  
  17.             <aop:after method="afterSleep" pointcut="execution(* *.sleep(..))" />  
  18.         </aop:aspect>  
  19.     </aop:config>  
  20.   
  21. </beans>  

(3)配置的另一種寫法

[html] view plain copy
  1. <aop:config>  
  2.     <aop:aspect ref="sleepHelper">  
  3.            <aop:pointcut id="sleepHelpers" expression="execution(* *.sleep(..))" />  
  4.            <aop:before pointcut-ref="sleepHelpers" method="beforeSleep" />  
  5.            <aop:after pointcut-ref="sleepHelpers" method="afterSleep" />            
  6.        </aop:aspect>  
  7. </aop:config>  


五、AOP實現原理

學東西還是要深入進去的,推薦一篇網評還不錯的博文,http://blog.csdn.net/moreevan/article/details/11977115/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章