伪代码:
@Override
@Transactional
public ResponseResult queryById(@RequestParam("id") Long id) {
return updateDevice(()->{
Date date = new Date();
log.info("----调用开始----{}",JSON.toJSONString(date));
BraceletDevice braceletDevice = deviceMapper.selectByPrimaryKey(id);
braceletDevice.setIsDelete(!braceletDevice.getIsDelete());
deviceMapper.updateByPrimaryKeySelective(braceletDevice);
if(braceletDevice == null){
return ResponseResult.responseSuccess();
}
return ResponseResult.responseSuccess(BeanMapper.copy(braceletDevice,BraceletDeviceResDto.class));
});
}
debug跟踪一下:
加上@Transactional注解的方法,在从外部访问时,都会经过一个拦截器TransactionInterceptor,很容易理解,spring启动时,通过方法上是否有注解@Transactional来作为事务拦截器的切面函数,有这个注解就会被切;
执行TransactionInterceptor拦截器,先进入invoke方法(我们自己手动使用代理类的时候也是这个方法),再执行私有方法invokeWithinTransaction
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
/**
* 一般委托基于环绕通知的一些子类,这里是委托给此类上的其他几个模板方法,
* 能够按照PlatformTransactionManager接口指定的规则那样,
* 去处理CallbackPreferringPlatformTransactionManager实现的具体业务,
* PlatformTransactionManager是抽象接口,各种事务都按它的规则来实现(如RabbitMQ)
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
分析invokeWithinTransaction方法:
首先,拿取事务属性源(搞啥用的?还不知道);
拿到事务属性源中的事务属性对象(包含事务的传播行为信息);
通过事务属性源选出合适的事务管理器;
通过类和方法去拿取连接点信息(一个字符串,像:类名.方法名 这样);
如果事务属性对象为null或者选出的事务管理器不是CallbackPreferringPlatformTransactionManager类
那就认为,没有开启事务,还是会有判断,如果当前线程已经存在事务?那就直接用这个事务,
如果事务的传播行为为PROPAGATION_REQUIRES_NEW,并且当前线程已经有事务了,那就挂起已存在线程,
…还有一连串的业务判断,我们最常用的就是事务合并了(默认的传播行为是合并)
然后会进行一次线程绑定(这一步如果有事务挂起,或其他事务存在情况,会暂存保留旧事务信息),以确保栈中事务信息正确
执行处理业务,反射执行真正的业务;
业务处理核心代码结构如下:执行,如果抛出异常,执行异常后事务处理业务,就是回滚(不是简单的回滚,如果我们在注解中配置了exception类型,那就只有遇到这种类型时才回滚,否则提交,不再执行后续的用户业务)
不管是否抛出异常,都执行清理事务信息方法(如果事务信息中保留有线程旧事务信息,这里会恢复旧事务信息到线程)
完事儿后,才是提交动作(如果有抛出异常,这一步也不走了)
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.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
你以为就提交了?
看看
/**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to {@code isRollbackOnly}, {@code doCommit}
* and {@code rollback}.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
processCommit(defStatus);
}
如果defStatus.isLocalRollbackOnly()返回true了,那就回滚,这个是啥呢,我们用得着吗?
public boolean isLocalRollbackOnly() {
return this.rollbackOnly;
}
用得着 我们写过手动控制事务回滚的代码,
比如:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
@Override
public void setRollbackOnly() {
this.rollbackOnly = true;
}
我们在开启事务的方法中,如果出于业务需要,需要回滚事务,那就用这句代码,现在可以理解这句代码的作用了,
我们是标识这个rollbackOnly字段,剩下的回滚依然是交由框架去处理;
那么,如果想手动处理事务,又想简单点,可以如下:
@Override
@Transactional
public ResponseResult queryById(@RequestParam("id") Long id) {
return updateDevice(()->{
Date date = new Date();
log.info("----调用开始----{}",JSON.toJSONString(date));
BraceletDevice braceletDevice = deviceMapper.selectByPrimaryKey(id);
braceletDevice.setIsDelete(!braceletDevice.getIsDelete());
deviceMapper.updateByPrimaryKeySelective(braceletDevice);
if(braceletDevice == null){
return ResponseResult.responseSuccess();
}
return ResponseResult.responseSuccess(BeanMapper.copy(braceletDevice,BraceletDeviceResDto.class));
});
}
/**
* 业务事务分离,手动控制事务
* @param supplier
* @return
*/
private ResponseResult updateDevice(Supplier> supplier){
ResponseResult responseResult = ResponseResult.responseSuccess();
try{
responseResult = supplier.get();
}catch (Exception e){
responseResult.setSuccess(false);
}finally {
if(!responseResult.isSuccess()){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.info("----事务回滚----");
}
return responseResult;
}
}
将事务和业务代码分开,利用lambda表达式,很优雅的实现和修改