在上面例子中的 UserServiceImpl 是使用编程式事务处理的,当没有加上 @Transactional 时,在 int i = 1 / 0; 处发生异常时,上面已经插入的用户信息并不会被回滚,这就不符合我们日常的业务需求了。OK,从这里开始将要开始看看 Spring 是如何应用事务到我们的代码上的。
/** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code@Transactional}. For example, other beans marked with Spring's * {@code@Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ booleanproxyTargetClass()defaultfalse;
/** * Indicate how transactional advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode()default AdviceMode.PROXY;
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ intorder()default Ordered.LOWEST_PRECEDENCE;
// META-INF/additional-spring-configuration-metadata.json { "name":"spring.aop.proxy-target-class", "type":"java.lang.Boolean", "description":"Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).", "defaultValue":true },
// 在Callback这里即会织入BeanFactoryTransactionAttributeSourceAdvisor中的拦截器: // TransactionInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = newClass<?>[callbacks.length]; for (intx=0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(newProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException IllegalArgumentException ex) { thrownewAopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed thrownewAopConfigException("Unexpected AOP exception", ex); } }
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Objectresult= ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfotxInfo= prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { ObjectretVal= invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { thrownewThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; returnnull; } } 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; } } }
// 未提交读 intISOLATION_READ_UNCOMMITTED=1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 不可重复读 intISOLATION_READ_COMMITTED=2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读 intISOLATION_REPEATABLE_READ=4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行执行事务 intISOLATION_SERIALIZABLE=8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/** * Use the default timeout of the underlying transaction system, * or none if timeouts are not supported. */ intTIMEOUT_DEFAULT= -1;
/** * Return the propagation behavior. * <p>Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. * <p>The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ defaultintgetPropagationBehavior() { return PROPAGATION_REQUIRED; }
/** * Return the isolation level. * <p>Must return one of the {@code ISOLATION_XXX} constants defined on * {@link TransactionDefinition this interface}. Those constants are designed * to match the values of the same constants on {@link java.sql.Connection}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. Consider switching the "validateExistingTransactions" flag to * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. * <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager * that does not support custom isolation levels will throw an exception when * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ defaultintgetIsolationLevel() { return ISOLATION_DEFAULT; }
/** * Return the transaction timeout. * <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started * transactions. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * <p>The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ defaultintgetTimeout() { return TIMEOUT_DEFAULT; }
/** * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context, whether backed * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, * the flag will only apply to managed resources within the application, * such as a Hibernate {@code Session}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ defaultbooleanisReadOnly() { returnfalse; }
/** * Return the name of this transaction. Can be {@code null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor, if applicable (for example, WebLogic's). * <p>In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable default String getName() { returnnull; }
// Static builder methods
/** * Return an unmodifiable {@code TransactionDefinition} with defaults. * <p>For customization purposes, use the modifiable * {@link org.springframework.transaction.support.DefaultTransactionDefinition} * instead. * @since 5.2 */ static TransactionDefinition withDefaults() { return StaticTransactionDefinition.INSTANCE; }
@Nullable protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) { // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == nullthis.beanFactory == null) { return getTransactionManager(); }
// 修改Connection的只读状态 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); }
// 如果当前是只读事务则发送 SET TRANSACTION READ ONLY 给数据库切换事务状态 prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true);
inttimeout= determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }
// 把ConnectionHolder绑定到TransactionSynchronizationManager if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } thrownewCannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
@Override publicfinalvoidcommit(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownewIllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { thrownewUnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; }
// Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // 调用事务生命周期的后处理器 triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); }
} finally { cleanupAfterCompletion(status); } }
拿到 Connection 调用 Commit 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoCommit(DefaultTransactionStatus status) { DataSourceTransactionObjecttxObject= (DataSourceTransactionObject) status.getTransaction(); Connectioncon= txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { thrownewTransactionSystemException("Could not commit JDBC transaction", ex); } }
protectedvoidcompleteTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 判断当前抛出的异常是否需要回滚数据库 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } // 如果不会滚,继续提交 else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
好吧来到了处理方法:
1 2 3 4 5 6 7 8 9 10
@Override publicfinalvoidrollback(TransactionStatus status)throws TransactionException { if (status.isCompleted()) { thrownewIllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); }
// Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { thrownewUnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { cleanupAfterCompletion(status); } }
拿到 Connection 进行回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override protectedvoiddoRollback(DefaultTransactionStatus status) { DataSourceTransactionObjecttxObject= (DataSourceTransactionObject) status.getTransaction(); Connectioncon= txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { con.rollback(); } catch (SQLException ex) { thrownewTransactionSystemException("Could not roll back JDBC transaction", ex); } }