采用AOP配置声明式事务有5种方式,下面只说关于采用TransactionInterceptor事务拦截器的方式,配置程序如下:
transactionManager:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
TransactionInterceptor:
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="del*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="query*">readOnly</prop>
<prop key="get*">readOnly</prop>
<prop key="find*">readOnly</prop>
<prop key="check*">PROPAGATION_REQUIRED</prop>
<prop key="operate*">PROPAGATION_REQUIRED</prop>
<prop key="batch*">PROPAGATION_REQUIRED</prop>
<prop key="deploy*">PROPAGATION_REQUIRED</prop>
<prop key="exec*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
自动代理BeanNameAutoProxyCreator:
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<!-- 所有以BUSImpl命名的Bean-Id都会被事务拦截-->
<value>*BUSImpl</value>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
业务类例子:
public class UserManageBUSImpl implements IBusiness{
private UserDAO dao;
public void addUser(User user) throws Exception{
dao.save(user);
}
}
public class UserDAO implements IDAO{
private JdbcTemplate db;
public void save(User user) throws Exception{
db.update("insert into User(...) values(...)");
throw new Exception("test exception"); // 这里我们故意抛出异常作为测试
}
}
然后运行发现记录仍然保存进去了,事务失效;
why?
我们首先应该知道使用事务回滚和提交,归根结底是在JDBC里完成的,这里声明事务拦截器仅是为JDK代理切入点拦截。而做事务提交和回滚是transactionManager完成的事。那么断点跟进拦截器里程序发现:
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be <code>null</code>.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final String joinpointIdentification = methodIdentification(invocation.getMethod());
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
......
completeTransactionAfterThrowing(txInfo, ex);这句话是异常捕获后做的事情,那么再跟进发现:
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isDebugEnabled()) {
logger.debug("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
}
if (txInfo.transactionAttribute.rollbackOn(ex)) { // 需满足这个条件
try {
this.transactionManager.rollback(txInfo.getTransactionStatus()); // 这里才完成JDBC事务回滚
} catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
} catch (Error err) {
logger.error("Application exception overridden by rollback error", ex); throw err; }
}
......
看来离真相越来越接近了,txInfo.transactionAttribute是什么呢?查看源码对应到一个接口TransactionAttribute,文档如下:
/**
* This interface adds a <code>rollbackOn</code> specification to TransactionDefinition.
* As custom <code>rollbackOn</code> is only possible with AOP, this class resides
* in the AOP transaction package.
*
* @author Rod Johnson
* @since 16.03.2003
* @see DefaultTransactionAttribute
* @see RuleBasedTransactionAttribute
*/
public interface TransactionAttribute extends TransactionDefinition {
/**
* Should we roll back on the given exception?
* @param ex the exception to evaluate
* @return whether to perform a rollback or not
*/
boolean rollbackOn(Throwable ex);
}
看下RuleBasedTransactionAttribute里实现的接口方法:
public boolean rollbackOn(Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
// 看来这里是要满足自定义回滚规则
for (Iterator it = this.rollbackRules.iterator(); it.hasNext();) {
RollbackRuleAttribute rule = (RollbackRuleAttribute) it.next();
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth; winner = rule;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
if (winner == null) {
logger.debug("No relevant rollback rule found: applying superclass default");
return super.rollbackOn(ex); // 如果没有规则,则调用父类方法验证回滚规则
}
return !(winner instanceof NoRollbackRuleAttribute); }
其父类方法为:
/**
* Default behavior is as with EJB: rollback on unchecked exception.
* Additionally attempt to rollback on Error.
* Consistent with TransactionTemplate's behavior.
*/
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error); // 最终是这个原因
}
原因:
由于业务类里抛出的异常不满足事务拦截器里定义的异常(RuntimeException|Error)事务回滚规则,故事务无效;
解决方案:
1,将业务类的抛出异常改为满足拦截器里的异常规则(不推荐,因为要修改以前所有的代码)
2,(推荐方案)在事务拦截器里声明自定义回滚规则,即this.rollbackRules.iterator()中有你自己申明的异常类,这个方案仅需在spring中配置如下:
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="del*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="update*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="query*">readOnly</prop>
<prop key="get*">readOnly</prop>
<prop key="find*">readOnly</prop>
<prop key="check*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="operate*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="batch*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="deploy*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="exec*">PROPAGATION_REQUIRED, -Exception</prop>
</props>
</property>
</bean>
分享到:
相关推荐
用springboot 集成mybatis多数据源,用aop实现的动态切换,支持事务,不会使aop动态切换失效。注意:此代码不涉及分布式事务,如果需要分布式事务 需要采取其他方案。
在Spring AOP代理过的对象上支持dubbo annotation配置事物失效问题-附件资源
1.1 Spring事务处理时自我调用的解决方案及一些实现方式的风险 1.2 我对AOP的理解 1.3 Spring开闭原则的表现-BeanPostProcessor的扩展点-1 1.4 我对IoC/DI的理解 1.5 SpringMVC + spring3.1.1 + hibernate4.1.0 集成...
设置Bean使自动装配失效 3.3.7. 依赖检查 3.3.8. 方法注入 3.3.8.1. Lookup方法注入 3.3.8.2. 自定义方法的替代方案 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. 其他作用域 3.4.3.1....
spring使用AOP面向切面的思想进行事务管理的。 spring和Hibernate继承后,定义事务管理特性的时候查询为什么要定义为read-only? 答:因为添加、删除和更新都涉及到了数据库的修改,而查询并未涉及到数据库修改,...
使用SpringFramework和AspectOrientedProgramming(AOP)原则,我们可以将关注点分离,以用于服务实现。本文中我们将演示如何使用ApacheAxis和Spring来开发Webservice,并使用AcegiSecurity对其进
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................