spring transaction and aop
<一> Transaction 前段時間對Spring的事務配置做了比較深入的研究,在此之前對Spring的事務配置雖說也配置過,但是一直沒有一個清楚的認識。通過這次的學習發覺Spring的事務配置隻要把思路理清,還是比較好掌握的。 總結如下: Spring配置檔案中關于事務配置總是由三個組成部分,分别是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的隻是代理機制這部分。 DataSource、TransactionManager這兩部分隻是會根據資料通路方式有所變化,比如使用Hibernate進行資料通路時,DataSource實際為SessionFactory,TransactionManager的實作為HibernateTransactionManager。 具體如下圖: 根據代理機制的不同,總結了五種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(); } } <二> aop 此前對于AOP的使用僅限于聲明式事務,除此之外在實際開發中也沒有遇到過與之相關的問題。最近項目中遇到了以下幾點需求,仔細思考之後,覺得采用AOP 來解決。一方面是為了以更加靈活的方式來解決問題,另一方面是借此機會深入學習Spring AOP相關的内容。本文是權當本人的自己AOP學習筆記,以下需求不用AOP肯定也能解決,至于是否牽強附會,仁者見仁智者見智。
終于下定決心,采用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的重要概念,結合以上代碼分析如下:
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代碼
<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方法定義如下:
類型。 接口提供了一系列有用的方法, 比如 (傳回方法參數)、 (傳回代理對象)、 (傳回目标)、 (傳回正在被通知的方法相關資訊)和 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 的關鍵工作尋找切面 ,或者說自己建立切面 。這個是可以按需所求的,就是說你可以設計自己需要功能的切面)。那我就建立一個安全類吧:但為了展現面向接口程式設計的思想,我們想抽取成接口然後在通過接口的實作類來實作這一功能: 建立接口:(這個是普通類的接口,也就是後面要使用切面的目标類的類接口)
所有原理我都注釋在源碼中。其中在 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 |
由于IE浏覽器更新禁用了alt+x快捷鍵,請用alt+q快捷鍵來快速進入寫說說入口 正在加載中...