我剛開始看Spring時用的書是林信良的《Spring技術手冊》,書中對聲明式事務主要採用 TransactionProxyFactoryBean,業務Bean如 Service或BO類繼承它來進行事務控制。代理工廠Bean( *ProxyFactoryBean)這一類Spring提供的工廠類,可將一個 bean 進行委託代理,從而達到控制方法行爲的目的。
此類事務配置一般如下(因爲我們未採用這種方式,就以書上的JDBC datasource舉例說明。不同公司採用的格式可能有所不同):
- <!-- 前面的 dataSource等配置略 ........ -->
- <!-- 事務管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource">
- <ref local="dataSource" />
- </property>
- </bean>
- <!-- 事務模板 -->
- <bean id="baseTransactionProxy"
- class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
- abstract="true">
- <property name="transactionManager" ref="transactionManager" />
- <property name="transactionAttributes">
- <props>
- <prop key="*">PROPAGATION_REQUIRED</prop> <!-- 本文僅簡單用一種方式說明。所有方法起事務,可以修改以精細和區別性控制事務 -->
- </props>
- </property>
- </bean>
- <!-- 業務對象 -->
- <bean id="authService" parent="baseTransactionProxy">
- <property name="target">
- <bean class="com.xxxx.cms.service.AuthorityService">
- <property name="authDao" ref="authDao" />
- </bean>
- </property>
- </bean>
- <bean id="departmentService" parent="baseTransactionProxy">
- <property name="target">
- <bean class="com.xxxx.cms.service.pojo.DepartmentService">
- <property name="departmentDao" ref="departmentDao" />
- </bean>
- </property>
- </bean>
- <!-- 數據訪問對象 -->
- <bean id="authDao" class="com.xxxx.cms.dao.jdbc.AuthorityDao">
- <property name="dataSource" ref="dataSource" />
- </bean>
- <bean id="departmentDao" class="com.xxxx.cms.dao.jdbc.DepartmentDao">
- <property name="dataSource" ref="dataSource" />
- </bean>
這種代理工廠Bean的事務管理一般都要求將 Service 的 bean 配置爲 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" , 上面的例子使用了 parent="baseTransactionProxy" 繼承機制簡化了原書中的配置。
代碼中通過以下方式使用:
- AuthService authService = (AuthService) context.getBean("authService");
- boolean b = authService.hasPermission("TOKEN_XXXXX");
AuthService authService = (AuthService) context.getBean("authService");
boolean b = authService.hasPermission("TOKEN_XXXXX");
2. 自動代理事務(*AutoProxyCreator) + Service + DAO配置
*AutoProxyCreator這一類東東,能夠自動爲Spring容器中的bean進行 AOP 代理,配置起來能適當簡化。一般需要用 BeanNameAutoProxyCreator 來配置對那些類進行自動代理, MethodPointcutAdvisor 來配置對哪些方法進行代理。
這種聲明式事務配置採用攔截器(Interceptor)或通知器(Advisor) 進行事務控制,這裏使用了Spring提供的 TransactionInterceptor 來管理事務。 (本質上 ProxyFactoryBean 也要生成被代理對象的字節碼,不過每個類型的ProxyFactoryBean 只能固定處理一個 Aspect,不算真正的AOP)。
以下配置對 *Service 的所有方法進行事務控制。
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
- <!-- 事務管理攔截器 -->
- <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
- <property name="transactionManager">
- <ref local="transactionManager"/>
- </property>
- <property name="transactionAttributes">
- <props>
- <prop key="*">PROPAGATION_REQUIRED</prop> <!-- 本文僅簡單用一種方式說明。所有方法起事務,還可以精細控制事務 -->
- </props>
- </property>
- </bean>
- <!-- 配置要攔截哪些方法 -->
- <bean id="trasactionMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
- <property name="mappedNames">
- <list>
- <value>*</value> <!-- 所有方法 -->
- </list>
- </property>
- <property name="advice">
- <ref local="transactionInterceptor" />
- </property>
- </bean>
- <!-- 配置要攔截哪些類,並使用那些攔截器 -->
- <bean id="ServiceAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
- <property name="proxyTargetClass" value="true"></property>
- <property name="beanNames">
- <list>
- <value>*Service</value>
- </list>
- </property>
- <property name="interceptorNames">
- <list>
- <!-- 頭三個是我們項目中用的其他 Advisor,這裏展示了添加攔截器進行aspect控制的靈活性。省略他們的配置 -->
- <value>monitorMethodPointcutAdvisor</value>
- <value>asynmonitorMethodPointcutAdvisor</value>
- <value>businessLogMethodPointcutAdvisor</value>
- <value>trasactionMethodPointcutAdvisor</value> <!-- 事務攔截器, 直接配成 transactionInterceptor 去掉 trasactionMethodPointcutAdvisor bean 也可以, -->
- </list>
- </property>
- </bean>
Service,DAO的配置方式稍微改變一下, Service不再作爲ProxyFactoryBean的 target屬性,而是一個頂級 bean,這樣 Service bean看起來就更單純一些。
- <!-- 業務對象 -->
- <bean class="com.xxxx.cms.service.AuthorityService">
- <property name="authDao" ref="authDao" />
- </bean>
- <bean class="com.xxxx.cms.service.pojo.DepartmentService">
- <property name="departmentDao" ref="departmentDao" />
- </bean>
- <!-- 數據訪問對象 -->
- <bean id="authDao" class="com.xxxx.cms.dao.jdbc.AuthorityDao">
- <property name="dataSource" ref="dataSource" />
- </bean>
- <bean id="departmentDao" class="com.xxxx.cms.dao.jdbc.DepartmentDao">
- <property name="dataSource" ref="dataSource" />
- </bean>
代碼中的使用方式不變,同 1 小節。
3. 自動代理 + Service DAO零配置
這種是我們項目中運用的方式,transactionInterceptor,trasactionMethodPointcutAdvisor ,ServiceAutoProxyCreator 三個 bean的配置不變。
Service,DAO 無需配置, 其原理參見我另一篇文章:
代碼中的使用方式不同,使用 ServiceFactory 直接創建bean,代碼如下:
- AuthService authService = (AuthService)ServiceFactory.createBean(AuthService.class);
- boolean b = authService.hasPermission("TOKEN_XXXXX");
AuthService authService = (AuthService)ServiceFactory.createBean(AuthService.class);
boolean b = authService.hasPermission("TOKEN_XXXXX");
ServiceFactory 內部會自動註冊未註冊的 bean, 並和Spring 容器和已配置的攔截器關聯。
4. Annotation 聲明事務
如果是 JDK 1.5下,Spring 也支持 註釋聲明式事務,類似於如下代碼(附部分片段):
- @Transactional
- public class PersonServiceImpl implements PersonService
- { private EntityManager em;
- @PersistenceContext
- public void setEntityManager(EntityManager em)
- { this.em = em; }
@Transactional
public class PersonServiceImpl implements PersonService
{ private EntityManager em;
@PersistenceContext
public void setEntityManager(EntityManager em)
{ this.em = em; }
結合 <tx:annotation-driven/>配置。
它實際是將 xml 中的
- <property name="transactionAttributes">
- <props>
- <prop key="*">PROPAGATION_REQUIRED</prop> <!-- 所有方法起事務,還可以精細控制事務 -->
- </props>
- </property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop> <!-- 所有方法起事務,還可以精細控制事務 -->
</props>
</property>
轉移到了Java代碼中,本質上是一樣的,兩種方式都能進行精細事務管理。不過個人不太偏好這種方式,因爲xml更便於集中管理,修改後無需編譯。