前言
spring声明式事务有三种实现方式:基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式以及注解的方式。
还是采用经典案例 银行转账 来构建代码,假设张三和李四账户都有1000元,现在张三向李四转账200元,观察spring是怎么管理事务的。
基于TransactionProxyFactoryBean的方式
一、代码示例
① dao类:
/**
* 创建人:taofut
* 创建时间:2019-01-08 19:31
* 描述:
*/
public interface AccountDao {
/**
* @param out 转出账号
* @param money 转账金额
*/
public void outMoney(String out, Double money);
/**
* @param in 转入账号
* @param money 转账金额
*/
public void inMoney(String in, Double money);
}
② dao实现类:
/**
* 创建人:taofut
* 创建时间:2019-01-08 19:33
* 描述:
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/**
* @param out 转出账号
* @param money 转账金额
*/
public void outMoney(String out, Double money) {
String sql="update account set money = money - ? where name = ?";
this.getJdbcTemplate().update(sql,money,out);
}
/**
* @param in 转入账号
* @param money 转账金额
*/
public void inMoney(String in, Double money) {
String sql="update account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql,money,in);
}
}
③ service类:
/**
* 创建人:taofut
* 创建时间:2019-01-08 19:26
* 描述:转账业务接口
*/
public interface AccountService {
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transfer(String out, String in, Double money);
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transferException(String out, String in, Double money);
}
④ service实现类:
/**
* 创建人:taofut
* 创建时间:2019-01-08 19:29
* 描述:
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transfer(String out, String in, Double money) {
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transferException(String out, String in, Double money) {
accountDao.outMoney(out,money);
int i = 1 / 0;
accountDao.inMoney(in,money);
}
}
⑤ 配置文件applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 数据库连接池 -->
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
</bean>
<!--配置业务层类-->
<bean id="accountService" class="com.ft.demo2.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!--配置dao层类-->
<bean id="accountDao" class="com.ft.demo2.AccountDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置业务层代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--配置目标对象-->
<property name="target" ref="accountService"/>
<!--注入事务管理器-->
<property name="transactionManager" ref="transactionManager"/>
<!--注入事务属性-->
<property name="transactionAttributes">
<props>
<!--
prop格式:
* Propagation:事务的传播行为
* Isolation:事务的隔离级别
* Readonly:只读(不可以进行修改、插入、删除)
* -Exception:发生哪些异常事务回滚
* +Exception:发生哪些异常事务不回滚
-->
<prop key="transfer*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
二、spring事务所需条件
① 资源文件里面只需要配置事务管理器和业务层代理类,这里采用TransactionProxyFactoryBean代理类。类里面注入事务管理器、目标对象、事务属性。
<!--配置业务层代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--配置目标对象-->
<property name="target" ref="accountService"/>
<!--注入事务管理器-->
<property name="transactionManager" ref="transactionManager"/>
<!--注入事务属性-->
<property name="transactionAttributes">
<props>
<!--
prop格式:
* Propagation:事务的传播行为
* Isolation:事务的隔离级别
* Readonly:只读(不可以进行修改、插入、删除)
* -Exception:发生哪些异常事务回滚
* +Exception:发生哪些异常事务不回滚
-->
<prop key="transfer*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
三、代码测试结果
该结果跟编程式事务结果一致。
四、总结
这里很明显dao和service层代码完全没改动,但是spring事务就已经加在方法上了,这体现了AOP编程思想。简而言之,就是在要加事务的方法代码前面做了些操作、代码后面做了些操作。这种方式非常灵活,使得业务层代码和事务耦合开来。
基于AspectJ的XML方式
一、代码示例
① dao和service代码都没动,跟上面的一致。
② 配置文件applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 数据库连接池 -->
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
</bean>
<!--配置业务层类-->
<bean id="accountService" class="com.ft.demo3.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!--配置dao层类-->
<bean id="accountDao" class="com.ft.demo3.AccountDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务的通知(事务的增强)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation:事务传播行为
isolation:事务隔离级别
read-only:只读
rollback-for:发生哪些异常回滚
no-rollback-for:发生哪些异常不回滚
timeout:过期信息
-->
<tx:method name="transfer*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.ft.demo3.AccountService+.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
</beans>
二、spring事务所需条件
① 资源文件里面只需要配置事务管理器和切面规则。切面规则包括:事务的增强(需要对哪个业务方法增加事务)和切面(哪些service下的方法需要增加事务)。
<!--配置事务的通知(事务的增强)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation:事务传播行为
isolation:事务隔离级别
read-only:只读
rollback-for:发生哪些异常回滚
no-rollback-for:发生哪些异常不回滚
timeout:过期信息
-->
<tx:method name="transfer*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.ft.demo3.AccountService+.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
三、代码测试结果
该结果跟编程式事务结果一致。
四、总结
这种方式在实际应用中特别广泛,它是一种自动代理的方式。也就是说在service类生成的过程中,它就已经增强了,自身就是一个代理类。
基于注解的方式
一、代码示例
① 需要在service实现类或方法上加上注解:
/**
* @Transactional中的属性:
* propagation:事务传播行为
* isolation:事务隔离级别
* readOnly:只读
* rollbackFor:发生哪些异常回滚
* noRollbackFor:发生哪些异常不回滚
*/
@Transactional
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transfer(String out, String in, Double money) {
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}
/**
* @param out 转出账号
* @param in 转入账号
* @param money 转账金额
*/
public void transferException(String out, String in, Double money) {
accountDao.outMoney(out,money);
int i = 1 / 0;
accountDao.inMoney(in,money);
}
}
② 配置文件applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 数据库连接池 -->
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
</bean>
<!--配置业务层类-->
<bean id="accountService" class="com.ft.demo4.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!--配置dao层类-->
<bean id="accountDao" class="com.ft.demo4.AccountDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
二、spring事务所需条件
① 资源文件里面只需要配置事务管理器和开启注解。
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
三、代码测试结果
该结果跟编程式事务结果一致。
四、总结
这种方式在实际应用中也很广泛,配置文件很简练,并且哪个方法需要使用事务就在哪个方法上面加上注解,十分方便。