天天看點

spring transaction and aop

       spring transaction and aop

spring transaction and aop

<一>  Transaction

前段時間對Spring的事務配置做了比較深入的研究,在此之前對Spring的事務配置雖說也配置過,但是一直沒有一個清楚的認識。通過這次的學習發覺Spring的事務配置隻要把思路理清,還是比較好掌握的。

    總結如下:

Spring配置檔案中關于事務配置總是由三個組成部分,分别是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的隻是代理機制這部分。

DataSource、TransactionManager這兩部分隻是會根據資料通路方式有所變化,比如使用Hibernate進行資料通路時,DataSource實際為SessionFactory,TransactionManager的實作為HibernateTransactionManager。

    具體如下圖:

spring transaction and aop

    根據代理機制的不同,總結了五種Spring事務的配置方式,配置檔案如下:

第一種方式:每個Bean都有一個代理

--> <? xml version="1.0" encoding="UTF-8" ?>

< beans xmlns ="http://www.springframework.org/schema/beans"

    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context ="http://www.springframework.org/schema/context"

    xmlns:aop ="http://www.springframework.org/schema/aop"

    xsi:schemaLocation ="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" >

< bean id ="sessionFactory"  

            class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > 

< property name ="configLocation" value ="classpath:hibernate.cfg.xml" /> 

< property name ="configurationClass" value ="org.hibernate.cfg.AnnotationConfiguration" />

</ bean > 

<!-- 定義事務管理器(聲明式的事務) --> 

< bean id ="transactionManager"

        class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

<!-- 配置DAO -->

< bean id ="userDaoTarget" class ="com.bluesky.spring.dao.UserDaoImpl" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

< bean id ="userDao"  

        class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" > 

<!-- 配置事務管理器 --> 

< property name ="transactionManager" ref ="transactionManager" />    

< property name ="target" ref ="userDaoTarget" /> 

< property name ="proxyInterfaces" value ="com.bluesky.spring.dao.GeneratorDao" />

<!-- 配置事務屬性 --> 

< property name ="transactionAttributes" > 

< props > 

< prop key ="*" >PROPAGATION_REQUIRED </ prop >

</ props > 

</ property > 

</ bean > 

</ beans >

第二種方式:所有Bean共享一個代理基類

--> <? xml version="1.0" encoding="UTF-8" ?>

< beans xmlns ="http://www.springframework.org/schema/beans"

    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context ="http://www.springframework.org/schema/context"

    xmlns:aop ="http://www.springframework.org/schema/aop"

    xsi:schemaLocation ="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" >

< bean id ="sessionFactory"  

            class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > 

< property name ="configLocation" value ="classpath:hibernate.cfg.xml" /> 

< property name ="configurationClass" value ="org.hibernate.cfg.AnnotationConfiguration" />

</ bean > 

<!-- 定義事務管理器(聲明式的事務) --> 

< bean id ="transactionManager"

        class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

< bean id ="transactionBase"  

            class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  

            lazy-init ="true" abstract ="true" > 

<!-- 配置事務管理器 --> 

< property name ="transactionManager" ref ="transactionManager" /> 

<!-- 配置事務屬性 --> 

< property name ="transactionAttributes" > 

< props > 

< prop key ="*" >PROPAGATION_REQUIRED </ prop > 

</ props > 

</ property > 

</ bean >   

<!-- 配置DAO -->

< bean id ="userDaoTarget" class ="com.bluesky.spring.dao.UserDaoImpl" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

< bean id ="userDao" parent ="transactionBase" > 

< property name ="target" ref ="userDaoTarget" />  

</ bean >

</ beans >

第三種方式:使用攔截器

--> <? xml version="1.0" encoding="UTF-8" ?>

< beans xmlns ="http://www.springframework.org/schema/beans"

    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context ="http://www.springframework.org/schema/context"

    xmlns:aop ="http://www.springframework.org/schema/aop"

    xsi:schemaLocation ="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" >

< bean id ="sessionFactory"  

            class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > 

< property name ="configLocation" value ="classpath:hibernate.cfg.xml" /> 

< property name ="configurationClass" value ="org.hibernate.cfg.AnnotationConfiguration" />

</ bean > 

<!-- 定義事務管理器(聲明式的事務) --> 

< bean id ="transactionManager"

        class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

< bean id ="transactionInterceptor"  

        class ="org.springframework.transaction.interceptor.TransactionInterceptor" > 

< property name ="transactionManager" ref ="transactionManager" /> 

<!-- 配置事務屬性 --> 

< property name ="transactionAttributes" > 

< props > 

< prop key ="*" >PROPAGATION_REQUIRED </ prop > 

</ props > 

</ property > 

</ bean >

< bean class ="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" > 

< property name ="beanNames" > 

< list > 

< value >*Dao </ value >

</ list > 

</ property > 

< property name ="interceptorNames" > 

< list > 

< value >transactionInterceptor </ value > 

</ list > 

</ property > 

</ bean > 

<!-- 配置DAO -->

< bean id ="userDao" class ="com.bluesky.spring.dao.UserDaoImpl" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

</ beans >

第四種方式:使用tx标簽配置的攔截器

--> <? xml version="1.0" encoding="UTF-8" ?>

< beans xmlns ="http://www.springframework.org/schema/beans"

    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context ="http://www.springframework.org/schema/context"

    xmlns:aop ="http://www.springframework.org/schema/aop"

    xmlns:tx ="http://www.springframework.org/schema/tx"

    xsi:schemaLocation ="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" >

< context:annotation-config />

< context:component-scan base-package ="com.bluesky" />

< bean id ="sessionFactory"  

            class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > 

< property name ="configLocation" value ="classpath:hibernate.cfg.xml" /> 

< property name ="configurationClass" value ="org.hibernate.cfg.AnnotationConfiguration" />

</ bean > 

<!-- 定義事務管理器(聲明式的事務) --> 

< bean id ="transactionManager"

        class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property name ="sessionFactory" ref ="sessionFactory" />

</ bean >

< tx:advice id ="" transaction-manager ="transactionManager" >

< tx:attributes >

< tx:method name ="*" propagation ="REQUIRED" />

</ tx:attributes >

</ tx:advice >

< aop:config >

< aop:pointcut id ="interceptorPointCuts"

            expression ="execution(* com.bluesky.spring.dao.*.*(..))" />

< aop:advisor advice-ref ="txAdvice"

            pointcut-ref ="interceptorPointCuts" />       

</ aop:config >     

</ beans >

第五種方式:全注解

--> <? xml version="1.0" encoding="UTF-8" ?>

< beans xmlns ="http://www.springframework.org/schema/beans"

    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context ="http://www.springframework.org/schema/context"

    xmlns:aop ="http://www.springframework.org/schema/aop"

    xmlns:tx ="http://www.springframework.org/schema/tx"

    xsi:schemaLocation ="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" >

< context:annotation-config />

< context:component-scan base-package ="com.bluesky" />

< tx:annotation-driven transaction-manager ="transactionManager" />

< bean id ="sessionFactory"  

            class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > 

< property name ="configLocation" value ="classpath:hibernate.cfg.xml" /> 

< property name ="configurationClass" value ="org.hibernate.cfg.AnnotationConfiguration" />

</ bean > 

<!-- 定義事務管理器(聲明式的事務) --> 

< bean id ="transactionManager"

        class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property name ="" ref ="sessionFactory" />

</ bean >

</ beans >

此時在DAO上需加上@Transactional注解,如下:

--> package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional

@Component("userDao")

public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

public List<User> listUsers() {

return this .getSession().createQuery("from User").list();

    }

spring transaction and aop

}

<二>  aop 

此前對于AOP的使用僅限于聲明式事務,除此之外在實際開發中也沒有遇到過與之相關的問題。最近項目中遇到了以下幾點需求,仔細思考之後,覺得采用AOP 來解決。一方面是為了以更加靈活的方式來解決問題,另一方面是借此機會深入學習Spring AOP相關的内容。本文是權當本人的自己AOP學習筆記,以下需求不用AOP肯定也能解決,至于是否牽強附會,仁者見仁智者見智。

  1. 對部分函數的調用進行日志記錄,用于觀察特定問題在運作過程中的函數調用情況
  2. 監控部分重要函數,若抛出指定的異常,需要以短信或郵件方式通知相關人員
  3. 金控部分重要函數的執行時間
    事實上,以上需求沒有AOP也能搞定,隻是在實作過程中比較郁悶擺了。
  1. 需要列印日志的函數分散在各個包中,隻能找到所有的函數體,手動添加日志。然而這些日志都是臨時的,待問題解決之後應該需要清除列印日志的代碼,隻能再次手動清除^_^!
  2. 類似1的情況,需要捕獲異常的地方太多,如果手動添加時想到很可能明天又要手動清除,隻能再汗。OK,該需求相對比較固定,屬于長期監控的範疇,并不需求臨時添加後再清除。然而,客戶某天要求,把其中20%的異常改為短信提醒,剩下的80%改用郵件提醒。改之,兩天後,客戶抱怨短信太多,全部改成郵件提醒...
  3. 該需求通常用于監控某些函數的執行時間,用以判斷系統執行慢的瓶頸所在。瓶頸被解決之後,煩惱同情況1

終于下定決心,采用AOP來解決!代碼如下:

切面類TestAspect

package com.spring.aop; public class TestAspect {

    public void doAfter(JoinPoint jp) {         System.out.println("log Ending method: "                 + jp.getTarget().getClass().getName() + "."                 + jp.getSignature().getName());     }

    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {         long time = System.currentTimeMillis();         Object retVal = pjp.proceed();         time = System.currentTimeMillis() - time;         System.out.println("process time: " + time + " ms");         return retVal;     }

    public void doBefore(JoinPoint jp) {         System.out.println("log Begining method: "                 + jp.getTarget().getClass().getName() + "."                 + jp.getSignature().getName());     }

    public void doThrowing(JoinPoint jp, Throwable ex) {         System.out.println("method " + jp.getTarget().getClass().getName()                 + "." + jp.getSignature().getName() + " throw exception");         System.out.println(ex.getMessage());     }

    private void sendEx(String ex) {         //TODO 發送短信或郵件提醒     } } 

package com.spring.service; public interface AService {          public void fooA(String _msg);

    public void barA(); }

  package com.spring.service; public class AServiceImpl implements AService {

    public void barA() {         System.out.println("AServiceImpl.barA()");     }

    public void fooA(String _msg) {         System.out.println("AServiceImpl.fooA(msg:"+_msg+")");     } }

package com.spring.service;

public class BServiceImpl {

    public void barB(String _msg, int _type) {         System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");         if(_type == 1)             throw new IllegalArgumentException("測試異常");     }

    public void fooB() {         System.out.println("BServiceImpl.fooB()");     }

}

ApplicationContext

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:aop="http://www.springframework.org/schema/aop"     xsi:schemaLocation="             http://www.springframework.org/schema/beans             http://www.springframework.org/schema/beans/spring-beans-2.0.xsd             http://www.springframework.org/schema/aop             http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"     default-autowire="autodetect"> <!--切面定義-->    

<aop:config>

        <!--切面處理器-->          <aop:aspect id="TestAspect" ref="aspectBean">             <!--配置com.spring.service包下所有類或接口的所有方法-->

        <!--切點-->              <aop:pointcut id="businessService"                 expression="execution(* com.spring.service.*.*(..))" />

        <!--通知-->              <aop:before pointcut-ref="businessService" method="doBefore"/>             <aop:after pointcut-ref="businessService" method="doAfter"/>             <aop:around pointcut-ref="businessService" method="doAround"/>             <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>         </aop:aspect>     </aop:config>          <bean id="aspectBean" class="com.spring.aop.TestAspect" />     <bean id="aService" class="com.spring.service.AServiceImpl"></bean>     <bean id="bService" class="com.spring.service.BServiceImpl"></bean>

</beans>

測試類AOPTest

public class AOPTest extends AbstractDependencyInjectionSpringContextTests { private AService aService; private BServiceImpl bService; protected String[] getConfigLocations() { String[] configs = new String[] { "/applicationContext.xml"}; return configs; } public void testCall() { System.out.println("SpringTest JUnit test"); aService.fooA("JUnit test fooA"); aService.barA(); bService.fooB(); bService.barB("JUnit test barB",0); } public void testThrow() { try { bService.barB("JUnit call barB",1); } catch (IllegalArgumentException e) { } } public void setAService(AService service) { aService = service; } public void setBService(BServiceImpl service) { bService = service; } }

運作結果如下:

log Begining method: com.spring.service.AServiceImpl.fooA AServiceImpl.fooA(msg:JUnit test fooA) log Ending method: com.spring.service.AServiceImpl.fooA process time: 0 ms log Begining method: com.spring.service.AServiceImpl.barA AServiceImpl.barA() log Ending method: com.spring.service.AServiceImpl.barA process time: 0 ms log Begining method: com.spring.service.BServiceImpl.fooB BServiceImpl.fooB() log Ending method: com.spring.service.BServiceImpl.fooB process time: 0 ms log Begining method: com.spring.service.BServiceImpl.barB BServiceImpl.barB(msg:JUnit test barB type:0) log Ending method: com.spring.service.BServiceImpl.barB process time: 0 ms

log Begining method: com.spring.service.BServiceImpl.barB BServiceImpl.barB(msg:JUnit call barB type:1) log Ending method: com.spring.service.BServiceImpl.barB method com.spring.service.BServiceImpl.barB throw exception 測試異常

    《Spring參考手冊》中定義了以下幾個AOP的重要概念,結合以上代碼分析如下:

  • 切面(Aspect) :官方的抽象定義為“一個關注點的子產品化,這個關注點可能會橫切多個對象”,在本例中,“切面”就是類TestAspect所關注的具體行為,例如,AServiceImpl.barA()的調用就是切面TestAspect所關注的行為之一。“切面”在ApplicationContext中<aop:aspect>來配置。
  • 連接配接點(Joinpoint) :程式執行過程中的某一行為,例如,AServiceImpl.barA()的調用或者BServiceImpl.barB(String _msg, int _type)抛出異常等行為。
  • 通知(Advice) :“切面”對于某個“連接配接點”所産生的動作,例如,TestAspect中對com.spring.service包下所有類的方法進行日志記錄的動作就是一個Advice。其中,一個“切面”可以包含多個“Advice”,例如TestAspect
  • 切入點(Pointcut) :比對連接配接點的斷言,在AOP中通知和一個切入點表達式關聯。例如,TestAspect中的所有通知所關注的連接配接點,都由切入點表達式execution(* com.spring.service.*.*(..))來決定
  • 目标對象(Target Object) :被一個或者多個切面所通知的對象。例如,AServcieImpl和BServiceImpl,當然在實際運作時,Spring AOP采用代理實作,實際AOP操作的是TargetObject的代理對象。
  • AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動态代理和CGLIB代理。預設情況下,TargetObject實作了接口時,則采用JDK動态代理,例如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。強制使用CGLIB代理需要将

    <aop:config>

    proxy-target-class

    屬性設為true
通知(Advice)類型
  • 前置通知(Before advice) :在某連接配接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接配接點前的執行。ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行聲明。例如,TestAspect中的doBefore方法
  • 後通知(After advice) :當某連接配接點退出的時候執行的通知(不論是正常傳回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行聲明。例如,TestAspect中的doAfter方法,是以AOPTest中調用BServiceImpl.barB抛出異常時,doAfter方法仍然執行
  • 傳回後通知(After return advice) :在某連接配接點正常完成後執行的通知,不包括抛出異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行聲明。
  • 環繞通知(Around advice) :包圍一個連接配接點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的調用前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行聲明。例如,TestAspect中的doAround方法。
  • 抛出異常後通知(After throwing advice) : 在方法抛出異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行聲明。例如,TestAspect中的doThrowing方法。
切入點表達式
  • 通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:
Java代碼
spring transaction and aop
spring transaction and aop
  1. execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

modifiers-pattern:方法的操作權限

ret-type-pattern:傳回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:參數名

throws-pattern:異常

其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,傳回值為任意類型;方法名任意;參數不作限制的所有方法。

  • 通知參數

可以通過args來綁定參數,這樣就可以在通知(Advice)中通路具體參數了。例如,<aop:aspect>配置如下

Java代碼

spring transaction and aop
spring transaction and aop
  1. <aop:config>  
  2.     <aop:aspect id="TestAspect" ref="aspectBean">  
  3.         <aop:pointcut id="businessService" 
  4.             expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />  
  5.             <aop:after pointcut-ref="businessService" method="doAfter"/>  
  6.     </aop:aspect>  
  7. </aop:config> 

<aop:config> <aop:aspect id="TestAspect" ref="aspectBean"> <aop:pointcut id="businessService" expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" /> <aop:after pointcut-ref="businessService" method="doAfter"/> </aop:aspect> </aop:config>

TestAspect的doAfter方法中就可以通路msg參數,但這樣以來AService中的barA()和BServiceImpl中的barB()就不再是連接配接點,因為execution(* com.spring.service.*.*(String,..))隻配置第一個參數為String類型的方法。其中,doAfter方法定義如下:

  1. public void doAfter(JoinPoint jp,String msg) 
public void doAfter(JoinPoint jp,String msg)
  • 通路目前的連接配接點
任何通知(Advice)方法可以将第一個參數定義為

org.aspectj.lang.JoinPoint

類型。

JoinPoint

接口提供了一系列有用的方法, 比如

getArgs()

(傳回方法參數)、

getThis()

(傳回代理對象)、

getTarget()

(傳回目标)、

getSignature()

(傳回正在被通知的方法相關資訊)和

toString()

JDK動态代理和CGLIB代理的差別  

    當然在實際運作時,Spring AOP采用代理實作,實際AOP操作的是TargetObject的代理對象。 

  JDK 的動态代理隻能對實作了接口的目标類進行代理,而不實作接口的類就不能使用 JDK 的動态代理

 CGLIB 是針對類來實作代理,當沒有實作接口的類需要代理時就需要通過 CGLIB 來實作代理了,他的原理是對指定的目标類生成一   個子類,并覆寫其中方法實作增強,但是因為采用的是繼承,是以不能對 finall 類進行繼承。  那如何配置 CGLIB 代理呢:

*<aop:aspect-autoproxy proxy-target-class=”true”/>

* 二者在某些特殊場合需混合使用    

通過Annotation來實作AOP

首先寫個題記吧 --- 如果你是 Spring 高手。那麼我所寫的文章對你可能一文不值 ( 至少最近幾篇 ) ,就等于說讓你看國小課本 ( 本人水準也一般 ) ;如果你覺得你是個新手,希望這篇文章沒有浪費你的時間,讓你有所收獲!

我覺得今天該寫寫所謂 Spring 中的 AOP 了,其實這并不是個什麼新鮮事物隻是 Spring 将它納入自己的架構取名曰: AOP( 面向切面程式設計 ) 。

在 Spring 中 AOP 有兩種實作技術:一種不太用的是通過 Annotation( 注解 ) 實作。另一種方法就是通過配置檔案來實作也就是我們的 configuration ,通過配置達到目的。 ( 這也是現在提倡的用法 ) 。

Annotation 方式實作 AOP ,首先得添加架包支援這兩個包就是: aspectjrt.jar 和 aspectjweave.jar 。當然你還得添加一些其他的 Spring 支援。我們通過代碼來來講解吧

因為是面向切面程式設計了,那知道橫切關注點了(假如我要實作安全驗證),那我們得将其抽取出來,子產品成一個類(也就是 AOP 的關鍵工作尋找切面 ,或者說自己建立切面 。這個是可以按需所求的,就是說你可以設計自己需要功能的切面)。那我就建立一個安全類吧:但為了展現面向接口程式設計的思想,我們想抽取成接口然後在通過接口的實作類來實作這一功能:

建立接口:(這個是普通類的接口,也就是後面要使用切面的目标類的類接口)

  1. package com.jummy.aop;    
  2. public interface UserManager {  
  3. public void  addUser(String string,String name);   
  4. public void delUser(int id);    
  5. public void modifyUser(int id,String name,int age);   
  6. }
  1. package com.jummy.aop;   
  2. public class UserManagerImpl implements UserManager   
  3. {     
  4. public void addUser(String id, String name)  
  5. {               
  6. System.out.println("---UsreManagerImpl中的addUser方法的實作-----");      
  7. }     
  8. public void delUser(int id)   
  9. {         
  10. System.out.println("-----delUser方法的實作-----");     
  11. }     
  12. public void modifyUser(int id, String name, int age)   
  13. {         
  14. System.out.println("----modifyUser方法的實作-----");       
  15. }   
  16. }
建立切面的接口:(所有的切面都可以從此接口繼承,這裡這樣寫主要是展現面向接口程式設計思想。這裡提供實作安全驗證的接口)
  1. package com.jummy.aop;    
  2. public interface MySecurityManager  
  3. {     public void security();       
  4. //也可以添加多個方法       
  5. //public void security2(); } 
建立切面 ( 類 ) :這是重點,除了實作接口中的方法以外,還需要在 Aspect 中定義 Pointcut 以及 Advice 。當然這一切是通過文章開頭說的 Aspectj 實作的,主要功能有兩個架包提供我們可直接引用( aspectjrt.jar 和 aspectjweave.jar )!
  1. import org.aspectj.lang.annotation.Aspect;  
  2. import org.aspectj.lang.annotation.Before;   
  3. import org.aspectj.lang.annotation.Pointcut; /  
  4. *  * 我們已經把橫切關注點抽取出來了  * 那就需要定義我們的Aspect了(類)  */   
  5. @Aspect //使用@Aspect之前記得要引入aspectjrt.jar和aspectjweaver.jar   
  6. public class MySecurityManagerImpl implements MySecurityManager   
  7. {          
  8. @Pointcut("execution(* add*(..))")    
  9.      private void addAllMethod() {   }     
  10. @Before("addAllMethod()")     
  11. public void security() {      
  12.     System.out.println("-----調用security方法-------");     }     

所有原理我都注釋在源碼中。其中在 Aspect 中需要做的就是定義 Pointcut 以及 Advice 。在定義 Pointcut 的過程中我們需要注意幾點:首先在 Aspectj 的支援下, @Pointcut(“execution(* add*(..))”) 來實作。表達式我不多講了自己看 Spring 的手冊去吧。解釋代碼中 private void addAllMethod(){} 這是一個沒有傳回值的空方法體,它程式的實際運作過程中并不執行 ( 同時我們也不希望它被其他人調用是以設定為 private 以權限控制 ) 。它隻是一個辨別,這個辨別會被下面代碼中的 Advice( 也就是 @before(“addAllMethod()”) 調用。它的作用類似于變量名,起一個被調用的載體的作用 ( 是以稱為辨別 ) ;當然切入點的内容 ( 也就是引号裡的一些表達式 ) 它們起到的作用就是來描述我們要切入哪些對象的哪些方法。

OK , pointcut 我們定義完成輪到 Advice 了。 Advice 是通過 @before(“addAllMethod()”) 來定義的,當然你可以是 after/throws 等等其他類型的通知。代碼 public void security(){-------} ;

這就是通知的具體方法實作,也就是我前面說的要添加的安全驗證。而且我申明了該驗證在我執行所有的以 add 開頭的方法之前執行。

   最後看看 spring 的配置檔案裡怎麼寫吧。

< aop:aspectj-autoproxy /> // 這裡就是申明添加 aspectj 的支援,跟導入一個包類似的作用

<!— 下面是切面的注入 -->

< bean id = "mySecurityManagerImpl" class = "com.jummy.aop.MySecurityManagerImpl" ></ bean >

  <!-- 這是目标對象的注入 -->

  < bean id = "userManagerImpl" class = "com.jummy.aop.UserManagerImpl" ></ bean >         

大家可能看到了,通過 Aspectj 對 Annotation 支援實作 , 所有的 Advice 啊 Pointcut 啊都是直接在代碼中實作的 當然裡面具體邏輯是 Spring 幫你通過代理來實作的,畢竟你自己的實作類中(如 UserManagerImpl 類中并沒有任何對安全性檢查的調用)

  可能有人要問了, Spring 不是還有其他的方式來實作 AOP 麼,你發現吧所有的 @Aspect 啊 @advice 啊 @before 都寫在切面類中太麻煩了,而且程式設計習慣也不好。是以就想到可以把它們都統統寫到配置檔案中來。通過 Spring 的配置檔案來統一管理。這就是平時大家最常用的采用配置方式來實作 AOP 。

注解:java.lang.annotation  

spring transaction and aop

由于IE浏覽器更新禁用了alt+x快捷鍵,請用alt+q快捷鍵來快速進入寫說說入口 正在加載中...