程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 讓Spring架構減化事務配置

讓Spring架構減化事務配置

編輯:關於JAVA

Spring顛覆了以前的編程模式,引入了IOC等全新的概念,廣受大家的喜愛。目前大多數j2ee項目都已經采用Spring框架。Spring最大的問題是太多的配置文件,使得你不僅需要維護程序代碼,還需要額外去維護相關的配置文件。最典型的就是事務配置(注:這裡的“事務配置”都指“聲明式事務配置”),在Spring中進行事務配置除了定義對象自身的bean外,還需要定義一個進行事務代理的bean.如果你有n個類需要引入事務,那麼你就必須定義2n個bean。維護這些bean的代價是十分昂貴的,所以必須要對事務配置進行減化。如果你是基於Spring進行架構設計,那麼作為一個好的架構設計師,應該把一些公共的方面進行簡化,讓項目的開發人員只關心項目的業務邏輯,而不要花費太多的精力去關心業務邏輯之外的太多東西。所以作為一個好的架構就應該把事務管理進行簡化,讓程序員花在編程之外的工作最小化。

1.Spring聲明式事務配置的幾種方法

在Spring中進行事務控制首先要選擇適當的事務管理器,其次為程序選擇劃分事務的策略。如果只有單個事務性資源,可以從“單一資源”的PlatformTransactionManger實現當中選擇一個,這些實現有:DataSourceTransactionManager,HibernateTransactionManager, JdoTransactionManager,PersistenceBrokerTransactionManager和JmsTransactionManager。根據你所采用的數據庫持久化技術選擇。如果你的項目運行於支持JTA的服務器,那麼將選擇JtaTransactionManger,將會支持多資源事務。

下表將為你選擇適當的事務管理器提供參考。

技術 事務管理器 內建的事務支持 JDBC DataSurceTransactionManager JtaTransactionManager JdbcTemplate和org.springframework.jdbc.object包中的所有類 IBATIS DataSourceTransactionManager JtaTransactionManager SqlMapClientTemplate和SqlClientTemplate Hibernate HibernateTransactionManager JtaTransactionManager HibernateTemplate和HibernateInterceptor JDO JdoTransactionManager JtaTransactionManager JdoTemplate和JdoInterceptor ApacheOJB PersistenceBrokerTransactionManager JtaTransactionManager PersistenceBrokerTemplate JMS JmsTransactionManager JmsTemplate

在劃分事務時,我們需要進行事務定義,也就是配置事務的屬性。事務的屬性有傳播行業,隔離級別,超時值及只讀標志。TransactionAttribute接口指定哪些異常將導致一個回滾,哪些應該一次性提交。

(1) 使用ProxyFactoryBean 和TransactionInterceptor

<!--定義本地數據源-->
  <bean id="dataSource" name="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>
<!-- !定義單個jdbc數據源的事務管理器-->
  <bean id="transactionManager"        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>
<!—定義攔截器-->
<bean id="transactionInterceptor"
     class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name="transactionManager">
      <ref bean="transactionManager"/>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="insert*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
        <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>
<!—定義業務對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService.target"
     class="com.prs.application.ehld.sample.biz.service.impl.SampleServiceImpl">
    <property name="userInfoDAO"
      ref="com.prs.application.ehld.sample.integration.dao.userInfoDAO">
     </property>
  </bean>
<!—定義業務對象的事務代理對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService" class="org.springframeword.aop.framework.ProxyFacgtoryBean">
    <property name="target"
      ref="com.prs.application.ehld.sample.biz.service.sampleService.target">
    </property>
    <property name="interceptorNames">
      <value>transactionInterceptor</value>
    </property>
  </bean>

通過ProxyFacgtoryBean和TransactionInterceptor組合使用,可以對事務進行更多的控制。所有需要事務控制的對象可以共享一個transactionInterceptor的事務屬性。

(2) 使用TransactionProxyFactoryBean

<!—定義業務對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService.target"
     class="com.prs.application.ehld.sample.biz.service.impl.SampleServiceImpl">
    <property name="userInfoDAO"
      ref="com.prs.application.ehld.sample.integration.dao.userInfoDAO">
     </property>
  </bean>
<!—定義業務對象的事務代理對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
     abstract="true">
    <property name="transactionManager">
      <ref bean="transactionManager"/>
    </property>
  <property name="target"
     ref="com.prs.application.ehld.sample.biz.service.sampleService.target" />
    <property name="transactionAttributes">
      <props>
        <prop key="insert*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
        <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>

使用TransactionProxyFactoryBean需要為每一個代理對象都去定義自己的事務屬性。

(3) 使用TransactionProxyFactoryBean及abstract屬性來簡化配置

這種方工也是目前使用得最多的一種聲明式事務配置方法

<!--事務控制代理抽象定義 -->
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    abstract="true">
    <property name="transactionManager">
      <ref bean="transactionManager"/>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="insert*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
        <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>
<!—定義業務對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService.target"
     class="com.prs.application.ehld.sample.biz.service.impl.SampleServiceImpl">
    <property name="userInfoDAO"
      ref="com.prs.application.ehld.sample.integration.dao.userInfoDAO">
     </property>
  </bean>
<!—定義業務對象的事務代理對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService" parent="baseTransactionProxy">
    <property name="target"
      ref="com.prs.application.ehld.sample.biz.service.sampleService.target">
     </property>
  </bean>

使用abstract屬性,可以讓代理對象可以共享一個定義好的事務屬性,使配置簡化。

(4)使用BeanNameAutoProxyCreator

<!—定義攔截器-->
<bean id="transactionInterceptor"
     class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name="transactionManager">
      <ref bean="transactionManager"/>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="insert*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
        <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>
<!—定義bean別名自動代理創建器-->
<bean id="autoProxyCreator"
     class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="interceptorNames">
       <value>transactionInterceptor</value>
    </property>
    <property name="beanNames">
        <list>
          <idref local="com.prs.application.ehld.sample.biz.service.sampleService"/>
        </list>
    </property>
  </bean>
<!—定義業務對象-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService"
     class="com.prs.application.ehld.sample.biz.service.impl.SampleServiceImpl">
    <property name="userInfoDAO"
      ref="com.prs.application.ehld.sample.integration.dao.userInfoDAO">
     </property>
  </bean>

使用BeanNameAutoProxyCreator可以由框架來提供適當的代理,由一個transactionInterceptor統一定義事務屬性,只需要把需要事務控制的bean加到beannames的列表。

對於需要大量聲明式事務的bean,BeanNameAutoProxyCreator是十分方便的。減少了代理bean的定義,還可以靈活的決定一個bean是否進行事務控制。

上面四種方法是在Spring中常見的聲明式事務配置方法,其中使用TransactionProxyFactoryBean及abstract屬性進行配置是最常見的簡化方法。

我們暫且把需要進行事務控制的bean叫事務bean.把依賴和調用它的bean叫做業務bean。對事務bean進行代理叫做事務代理bean.

1.使用ProxyFactoryBean 和TransactionInterceptor,可以由一個TransactionInterceptor統一定義事務屬性,對於每一個事務bean都需要再定義一個事務代理bean。如果有n個事務bean,那麼就需要定義和維護2n個bean。並且注入到業務bean的不是事務bean本身,而是要求用事務代理bean注入。這增加了理解的難度。

2.使用TransactionProxyFactoryBean需要為每一個事務代理bean都定義自己的事務屬性,除了需要維護2n個bean外,還需要為每一個事務代理bean定義事務屬性。可以說是最麻煩的。同樣需要把事務代理bean注入到業務bean,增加了理解的難度和項目的復雜度。

3.使用TransactionProxyFactoryBean及abstract屬性是對使用TransactionProxyFactoryBean的一種簡單化配置,可以讓所有的事務bean共享一致的事務屬性定義。需要維護2n個bean,需要把事務代理bean注入到業務bean。

4.使用BeanNameAutoProxyCreator最適合在框架中使用,只需要維護n個bean。也無需要事務代理bean。直接把事務bean注入業務bean中。但是它必須把需要事務控制的bean加到beanNames列表中。

類型自動代理創建器BeanClassTypeAutoProxyCreator

得於BeanNameAutoProxyCreator的啟示,BeanNameAutoProxyCreator可以實現框架來實現自動代理。它只是把需要代理的bean加入beanNames屬性列表。大大的簡化了代理的配置,減少了代理bean的定義,使用事務bean注入業務對象,而不是代理bean注入,更合乎事務邏輯。BeanNameAutoProxyCreator仍然需要開發人員除了定義業務bean外,還需要關心事務的定義,當然已經簡單了很多。如果能實現一個BeanClassTypeAutoProxyCreator,為它指定一個可以代理的ClassType列表,那麼在context中所有屬於ClassType和其子類的bean都自動獲得代理。

實現思路:

1.BeanNameAutoProxyCreator繼承了AbstractAutoProxyCreator,去實現方法:

protected abstract Object[] getAdvicesAndAdvisorsForBean(

Class beanClass, String beanName, TargetSource customTargetSource)

在BeanNameAutoProxyCreator中的實現是判斷beanName 是存在於beanNames列表,如果能找到則Object[]不對空。否則返回null。

所以BeanClassTypeAutoProxyCreator也應該繼承AbstractAutoProxyCreator。

getAdvicesAndAdvisorsForBean方法的實現可以參照BeanNameAutoProxyCreator方法的實現

2.BeanClassTypeAutoProxyCreator需要有一個進行代理的ClassType列表,在bean進行初始化後就在context中查找類型為ClassType列表中類型的所有beanName.從而獲得一個beanNames列表。

獲得beanNames列表後就可以像BeanNameAutoProxyCreator一樣實現自動代理了。

3.要想獲得當前context,我們可以實現ApplicationContextAware接口。讓BeanClassTypeAutoProxyCreator的bean可以獲得當前context.

4.要在bean進行初始化動作,可以實現InitializingBean接口,實現afterPropertiesSet,在這個方法中在context中根據classType查找獲得相關的beanName的列表。

5.寫一個空接口,裡面沒有任何方法。需要事務代理的類實現這個空接口。

這樣,只需要把這個空接口的全類名作為BeanClassTypeAutoProxyCreator的classTypes參數值,然後所有需要代理的類都去實現這個接口就可以自動獲得代理了。無再需要任何配置。這樣就可以讓程序員專心於業務邏輯的開發,而無需要去關心事務控制方法,就像是沒有使用事務一樣。

完整的實現類如下:
BeanClassTypeAutoProxyCreator.java
/**//**
* 根據類型自動代理Creator
*
* @author yuanguangdong date: Jul 13, 2004
*/
public class BeanClassTypeAutoProxyCreator extends AbstractAutoProxyCreator
    implements ApplicationContextAware, InitializingBean ...{
  /**//** Logger that is available to subclasses */
  protected final Log logger = LogFactory.getLog(getClass());
  /**//** ApplicationContext this object runs in */
  private ApplicationContext applicationContext;
  /**//** MessageSourceAccessor for easy message access */
  private MessageSourceAccessor messageSourceAccessor;
  /**//**被代理的bean別名列表**/
  private List beanNames;
  /**//**被代理的classType列表**/
  private List classTypes;
  //---------------------------------------------------------
  //實現AbstractAutoProxyCreator的抽像方法
  //---------------------------------------------------------
  /**//**
   * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean(java.lang.Class,
   *   java.lang.String, org.springframework.aop.TargetSource)
   */
  protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass,
      String beanName, TargetSource targetSource) throws BeansException ...{
    if (this.beanNames != null) ...{
      if (this.beanNames.contains(beanName)) ...{
        return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
      }
    }
    return DO_NOT_PROXY;
  }
  //-------------------------------------------------------
  //實現ApplicationContextAware接口方法
  //-------------------------------------------------------
  /**//**
   * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
   */
  public void setApplicationContext(ApplicationContext context)
      throws BeansException ...{
    if (context == null && !isContextRequired()) ...{
      // Reset internal context state.
      this.applicationContext = null;
      this.messageSourceAccessor = null;
    } else if (this.applicationContext == null) ...{
      // Initialize with passed-in context.
      if (!requiredContextClass().isInstance(context)) ...{
        throw new ApplicationContextException(
            "Invalid application context: needs to be of type ["
                + requiredContextClass().getName() + "]");
      }
      this.applicationContext = context;
      this.messageSourceAccessor = new MessageSourceAccessor(context);
      initApplicationContext();
    } else ...{
      // Ignore reinitialization if same context passed in.
      if (this.applicationContext != context) ...{
        throw new ApplicationContextException(
            "Cannot reinitialize with different application context: current one is ["
                + this.applicationContext
                + "], passed-in one is [" + context + "]");
      }
    }
  }
  /**//**
   * Determine whether this application object needs to run in an
   * ApplicationContext.
   * <p>
   * Default is "false".Can be overridden to enforce running in a context
   * (i.e.to throw IllegalStateException on accessors if outside a context).
   *
   * @see #getApplicationContext
   * @see #getMessageSourceAccessor
   */
  protected boolean isContextRequired() ...{
    return true;
  }
  /**//**
   * Determine the context class that any context passed to
   * <code>setApplicationContext</code> must be an instance of.Can be
   * overridden in subclasses.
   *
   * @see #setApplicationContext
   */
  protected Class requiredContextClass() ...{
    return ApplicationContext.class;
  }
  /**//**
   * Return the ApplicationContext instance used by this object.
   */
  public final ApplicationContext getApplicationContext()
      throws IllegalStateException ...{
    if (this.applicationContext == null && isContextRequired()) ...{
      throw new IllegalStateException(
          "ApplicationObjectSupport instance [" + this
              + "] does not run in an ApplicationContext");
    }
    return applicationContext;
  }
  /**//**
   * Return a MessageSourceAccessor for the application context used by this
   * object, for easy message access.
   *
   * @throws IllegalStateException
   *       if not running in an ApplicationContext
   */
  protected final MessageSourceAccessor getMessageSourceAccessor()
      throws IllegalStateException ...{
    if (this.messageSourceAccessor == null && isContextRequired()) ...{
      throw new IllegalStateException(
          "ApplicationObjectSupport instance [" + this
              + "] does not run in an ApplicationContext");
    }
    return this.messageSourceAccessor;
  }
  public void setClassTypes(String[] classTypes) ...{
    this.classTypes = Arrays.asList(classTypes);
  }
  /**//**
   * Subclasses can override this for custom initialization behavior.Gets
   * called by <code>setApplicationContext</code> after setting the context
   * instance.
   * <p>
   * Note: Does </i>not</i> get called on reinitialization of the context but
   * rather just on first initialization of this object's context reference.
   *
   * @throws ApplicationContextException
   *       in case of initialization errors
   * @throws BeansException
   *       if thrown by ApplicationContext methods
   * @see #setApplicationContext
   */
  protected void initApplicationContext() throws BeansException ...{
  }
  //-----------------------------------
  //實現InitializingBean接口方法
  //-----------------------------------
  /**//**
   * 查找指定classType的beanName列表
   */
  private List getBeanNames(String classType) ...{
    List beanNameList = null;
    try ...{
      String[] beanName = this.getApplicationContext()
          .getBeanNamesForType(Class.forName(classType), true, false);
      if (beanName != null) ...{
        beanNameList = Arrays.asList(beanName);
      }
    } catch (ClassNotFoundException ex) ...{
      throw new IllegalArgumentException("Class not found: "
          + ex.getMessage());
    }
    return beanNameList;
  }
  /**//**
   *
   * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
   */
  public void afterPropertiesSet() throws Exception ...{
    if (classTypes != null && !classTypes.isEmpty()) ...{
      beanNames = new ArrayList();
      for (int i = 0; i < classTypes.size(); i++) ...{
        String classType = (String) classTypes.get(i);
        List aList = getBeanNames(classType);
        beanNames.addAll(aList);
      }
    }
    if (logger.isDebugEnabled()) ...{
      for (int i = 0; i < beanNames.size(); i++) ...{
        logger.debug("printBean:" + (String) beanNames.get(i));
      }
    }
  }
}

使用BeanClassTypeAutoProxyCreator

3.1為了使用BeanClassTypeAutoProxyCreator,將為所有需要進行代理的類定一個接口。

package com.prs.application.ehld.biz.service;
public interface BaseService ...{
}

3.2 讓需要代理的類實現或繼承這個公共接口

package com.prs.application.ehld.sample.biz.service;
public interface SampleService extends BaseService ...{
    public void setUserInfoDAO(UserInfoDAO userInfoDAO);
    public void insertUserInfo(UserInfoDTO userInfo) throws BusinessServiceException;
}

3.3 配置事務代理

<!—定義攔截器-->
<bean id="transactionInterceptor"
     class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name="transactionManager">
      <ref bean="transactionManager"/>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="insert*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
        <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>
<!—定義類型自動代理創建器-->
<bean id="autoClassTypeProxyCreator"
    class="com.prs.application.ehld.common.aotoproxy.BeanClassTypeAutoProxyCreator">
   <property name="interceptorNames">
       <value>transactionInterceptor</value>
    </property>
    <property name="classTypes">
        <list>
          <value>com.prs.application.ehld.biz.service.BaseService</value>
        </list>
    </property>
  </bean>
<!—定義事務bean-->
<bean id="com.prs.application.ehld.sample.biz.service.sampleService"
     class="com.prs.application.ehld.sample.biz.service.impl.SampleServiceImpl">
    <property name="userInfoDAO"
      ref="com.prs.application.ehld.sample.integration.dao.userInfoDAO">
     </property>
  </bean>

效果:只需要定義BeanClassTypeAutoProxyCreator,把需要代理的類型BaseService作為classTypes的值。這樣任何實現了BaseService接口的類都自動獲得代理。使得程序員就像配置普通bean一樣去配置一個需要事務代理的bean。使得程序員只需要去關心業務邏輯。而無需要去關注事務這些框架應該支持的事情。特別是當開發團隊成員水平不一,或團隊人員流動性大時,BeanClassTypeAutoProxyCreator就發揮了它的作用。一個好的架構設計應該對事務控制,異常處理,日志記錄這些方面進行統一的規劃和處理,才能保證系統的健壯性。

采用Spring框架進行項目開發,我們在獲得它的IOC等好處,同時給我們增加了維護太多配置文件的負擔。應該盡量減少bean的定義,更多采用嵌套bean定義。否則將加大項目後期的維護成本。作為一個架構設計者更是應該把通用性比較強的方面進行統一規劃。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved