2013年1月20日 星期日

spring aop + transaction 控制

整個aop的目的在於 : 如果你會一直寫一樣的東西,那就抽出來,用動態的方式安插在需要的地方即可。

交易是在aop 中一定會被提及的部份,除非你的程式都是單純的報表查詢,否則交易需求是一定會被提到。

在這裡透過aop + annotation 可以超簡單的達成。

spring的xml中要有這幾樣東西。


  1. <context:annotation-config />
  2. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  3.         <property name="sessionFactory" ref="sessionFactory"></property>
  4.  </bean>
  5.  <tx:annotation-driven transaction-manager="transactionManager" />

最後一行是說,我要用過annotation的方式來設定transaction。如果不加這一行是無法達成用anno 的方式來設定。

(sessionFactory我假設你已經設定好了…)

再來,只要在你想要做交易的地方宣告@transactional即可。


  1.      @Transactional
  2.      public class UserService {
  3.        @Reso urce
  4.         private UsersDAO userDao;
  5.        
  6.        @Transactional
  7.         public List<Users> list() {
  8.             userDao.addUserErrorTest();
  9.             return userDao.listAll();
  10.         }
  11.         public void testInsertError(){
  12.            
  13.         }
  14. }


我userDao裡的這個方法,會故意新增一個成功,另一個失敗,當這樣宣告的時候,此方法裡呼叫的程式將會被視為一次交易,要就是全部成功,否則就是全部失敗。

aop + transaction的顆粒度極限是方法。所以在dao某個方法中,要重新思考,若不需要交易的程式區塊,要再重構出新的方法,畢竟交易是需要佔時間與資源的。

以下補充一下sessionFactory 的設定方式,我的範例是以 jndi 的方式來做。


  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  2.         <property name="dataSource" ref="dataSource" />
  3.         <property name="hibernateProperties">
  4.             <value>
  5.             hibernate.dialect=org.hibernate.dialect.SQLServerDialect
  6.               hibernate.show_sql=true
  7.             </value>
  8.         </property>
  9.         <property name="annotatedClasses">
  10.             <list>
  11.                 <value>entity.Users</value>
  12.             </list>
  13.         </property>
  14.     </bean>
  15.     <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  16.         <property name="jndiName" value="java:comp/env/jdbc/deu"></property>
  17.     </bean>

spring 3 aop 簡單實現 - 透過Annotation.

前言 : 若你是搭配 struts2的話,請取消繼承 ActionSupport。否則將會遇到java.lang.NoSuchMethodException: $Proxy..這一類的問題。


AOP 是幹麼的 ? 我一直覺得這個名詞翻譯的很不吸引人,導致很多人一聽到就退縮了。那至少你是聽過「注入」吧,就是 Spring標榜的核心功能。AOP 只要翻成「插入」就行了。雖然聽來有一點色情,不過先這樣翻譯比較吸引人。

物理上的插入還要定義方向,在AOP的術語來說,他是Cross-cutting,也就是橫的插進去。想像程式碼是一個磚頭,現在我在上面放一片木頭( 做log ),在下面也放一片木頭 (log ),我這個木頭是共用的,所以不用因為每一個磚頭就製造一片新的木頭。

我們再想一個案例 : 客戶反應執行某段程式的時候異常的慢,他們和你攤牌了 : 超過7秒的真的無法接受,請全面檢查。

主案眉頭一皺,請你把所有使用者「按下去」到得到結果的所有功能,中間執行時間做一個EXCEL出來,星期一開會檢討。

若沒有AOP 的話,好吧,你不會想拿手錶出來計時吧? 

如果有一個東西可以像盒子一樣,把你所有的程式像樂高積木裝進去,當進去要執行的時候紀錄一次時間,出來的時候紀錄一次時間,只要超過7秒就記在什麼LOG檔吧,那該有多好?

AOP 可以解決類似這樣的問題。畢竟你不想把所有的程式都在開始與結束寫一次,當然你可以透過攔截器或是什麼過濾器達到,不過AOP 的動配裝配蠻有趣的,不彷也可以了解一下。

首先我們要先寫一個紀錄時間的類別,這玩意叫Aspect,切入面,他是實際會插到程式裡的東西,所以你裡面自然會想到 : 前插、後插、前後插(環繞)、異常插等等,為了不要搞的太複雜就只說前插入與後插入。

先寫一個class

  1. package aop;
  2. import org.apache.log4j.Logger;
  3. import org.aspectj.lang.JoinPoint;
  4. import org.aspectj.lang.annotation.After;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Before;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. @Aspect
  9. public class LogExecuteTime {
  10.    
  11.    
  12.     @Pointcut("execution(* action.IndexAction.*(..))")
  13.     private void anyMethod(){}
  14.    
  15.     @Before("anyMethod()")
  16.     public void beforeAdvice(JoinPoint jp){
  17.         String noti = jp.getTarget().getClass().getName()+" "+jp.getSignature().getName();
  18.         System.out.println("before" + noti+" has been started.");
  19.    
  20.     }
  21.    
  22.     @After("anyMethod()")
  23.     public void afterAdvice(JoinPoint jp){
  24.         String noti = jp.getTarget().getClass().getName()+" "+jp.getSignature().getName();
  25.        
  26.         System.out.println("after" + noti+" has been executed.");
  27.     }
  28. }

這邊伴隨著annotation做直觀的設定。比較特別的是你要先定義哪些程式需要插入。

  1. @Pointcut("execution(* action.IndexAction.*(..))")
  2.     private void anyMethod(){}

這裡代表IndexAction這個類別下的所有方法都要被套用。
接下來你想要前插,就寫@before的方法,後插就寫@after的方法。
我們在軟體工程常常講去耦合,aop 就是一個超好的例子。怎麼說?
因為要被插入的類別中,完全不知道這件事的存在! 
也就是悄悄的來,悄悄的去,有一點像百貨公司的人數計算器一樣,進場的民眾不需要做什麼,但是臨時在入口裝一個計數器,就知道進場有多少人了。如果客戶、主管滿意後,這個aop甚至可以告一段落,主程式完全不受影響。

ok,但是在xml 中還是要把這個程式交由Spring管理,所以請先加

<aop:aspectj-autoproxy/> ( 這代表要使用aspectj)

再把計時器加入管理即可。
<bean id="logAspect" class="aop.LogExecuteTime" />


所有的耦合只存在於計時器的類別
  @Pointcut("execution(* action.IndexAction.*(..))")

所以當你要增加、減少控制項目時,非常的簡單。
我建議採用annotation甚於xml,看程式的時候即可一目了然,不需要再看xml來對照。

2013年1月19日 星期六

struts 2 +spring (含aop )+hibernate lib 設定

請參考前一篇將前二者的lib加入後。

針對hibernate的部份 :

spring :

spring-jdbc-3.2.0.RELEASE
spring-orm-3.2.0.RELEASE
spring-tx-3.2.0.RELEASE ( 以前好像叫spring-transaction-.....jar)
--for spring aop
spring-aop-3.2.0.RELEASE
spring-instrument-3.2.0.RELEASE
spring-context-support-3.2.0.RELEASE
spring-aspects-3.2.0.RELEASE
aopalliance.jar
aspectjweaver.jar
http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.7.1

hibernate: (在他下載包的其它目錄找找)
antlr-2.7.6.jar
jta-1.1
slf4j-api-1.6.1
hibernate-jpa-2.0-api-1.0.1.Final
hibernate3
dom4j-1.6.1

spring3.2 + struts2 的 lib 配置

spring 3.2 的配置上有一個差別。
也就是asm 這個獨立的jar檔要把它移除,因為asm已經被放在
spring-core-3.2.0.RELEASE.jar 中了。

要配置spring 3.2 + struts 2.3的話,最少需要這幾個jar 檔才能跑起來。

commons: (下載大包的struts壓縮檔中都有)
commons-beanutils-1.8.0
commons-collections-3.1
commons-fileupload-1.2.2
commons-io-2.0.1
commons-lang-2.4
commons-lang3-3.1
commons-logging-1.1.1

for spring: (當然是從spring的大包來)
spring-beans-3.2.0.RELEASE
spring-context-3.2.0.RELEASE
spring-core-3.2.0.RELEASE
spring-expression-3.2.0.RELEASE
spring-web-3.2.0.RELEASE

for struts2: ( from struts的大包)
struts2-core-2.3.8
struts2-spring-plugin-2.3.8
xwork-core-2.3.8

others: ( from struts的大包)
freemarker-2.3.19
javassist-3.11.0.GA
ognl-3.0.6