@Transactional注解

Spring事务时,通常加上事务注解来简化事务管理,这个事务注解是如何工作的呢?

伪代码:

    @Override
    @Transactional
    public ResponseResult<BraceletDeviceResDto> 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<BraceletDeviceResDto> 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<BraceletDeviceResDto> updateDevice(Supplier<ResponseResult<BraceletDeviceResDto>> supplier){
        ResponseResult<BraceletDeviceResDto> 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表达式,很优雅的实现和修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值