test1 run...Exception in thread "main"java.lang.ArithmeticException:/ by zero at com.example.demo.service.DemoService.test1(DemoService.java:12) at com.example.demo.service.DemoService$$FastClassBySpringCGLIB$$203c87bf.invoke(<generated>)
并且从控制台的异常信息栈中发现了cglib的身影,因为编写的 Service 没有接口,使用cglib创建的代理对象。
Implementation of the org.springframework.transaction.interceptor.TransactionAttributeSource interface for working with transaction metadata in JDK 1.5+ annotation format. This class reads Spring's JDK 1.5+ Transactional annotation and exposes corresponding transaction attributes to Spring's transaction infrastructure. Also supports JTA 1.2's javax.transaction.Transactional and EJB3's javax.ejb.TransactionAttribute annotation (if present). This class may also serve as base class for a custom TransactionAttributeSource, or get customized through TransactionAnnotationParser strategies.
public Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex ==this.interceptorsAndDynamicMethodMatchers.size() -1) {returninvokeJoinpoint(); } Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// ...... }else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}
publicObjectinvoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be 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...returninvokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}
protectedObjectinvokeWithinTransaction(Method method, @NullableClass<?> targetClass,finalInvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas =getTransactionAttributeSource();// 获取@Transactional的属性配置finalTransactionAttribute txAttr = (tas !=null?tas.getTransactionAttribute(method, targetClass) :null);// 获取事务管理器(IOC容器中获取)finalPlatformTransactionManager tm =determineTransactionManager(txAttr);finalString 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;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 {// 与上面相似,省略。。。 }}
publicfinalvoidcommit(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {thrownewIllegalTransactionStateException("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);}
2.4.1.3 processCommit
privatevoidprocessCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked =false;try {boolean unexpectedRollback =false;prepareForCommit(status);triggerBeforeCommit(status);triggerBeforeCompletion(status); beforeCompletionInvoked =true;// 是否有保存点if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint"); } unexpectedRollback =status.isGlobalRollbackOnly();// 保存点不会真正提交事务status.releaseHeldSavepoint(); }// 全新的事务会执行commit操作(与事务传播行为有关)elseif (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit"); } unexpectedRollback =status.isGlobalRollbackOnly();doCommit(status); }elseif (isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback =status.isGlobalRollbackOnly(); }// 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 ...... }finally {// 清除缓存资源cleanupAfterCompletion(status); }}
protectedvoidcompleteTransactionAfterThrowing(@NullableTransactionInfo txInfo,Throwable ex) {// 回滚必须要保证当前正在一个事务中if (txInfo !=null&&txInfo.getTransactionStatus() !=null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for ["+txInfo.getJoinpointIdentification() +"] after exception: "+ ex); }// 4.2.1.1 回滚的事务必须为RuntimeException或Error类型if (txInfo.transactionAttribute!=null&&txInfo.transactionAttribute.rollbackOn(ex)) {try {// 回滚事务txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); }// catch ...... }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 ...... }}}
在回滚之前,它要判定抛出的异常类型。
2.4.2.1.1 rollbackOn
publicbooleanrollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);}
publicfinalvoidrollback(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {thrownewIllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); }DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;processRollback(defStatus,false);}
protectedvoiddoRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con =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); }}
protectedObjectinvokeWithinTransaction(Method method, @NullableClass<?> targetClass,finalInvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas =getTransactionAttributeSource();finalTransactionAttribute txAttr = (tas !=null?tas.getTransactionAttribute(method, targetClass) :null);finalPlatformTransactionManager tm =determineTransactionManager(txAttr);finalString joinpointIdentification =methodIdentification(method, targetClass, txAttr);if (txAttr ==null||!(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.// 1.2 如果当前执行方法需要事务,则开启事务TransactionInfo txInfo =createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal;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 exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex; }finally {cleanupTransactionInfo(txInfo); }commitTransactionAfterReturning(txInfo);return retVal; }// ......}
Resource holder wrapping a JDBC Connection. DataSourceTransactionManager binds instances of this class to the thread, for a specific javax.sql.DataSource. Inherits rollback-only support for nested JDBC transactions and reference count functionality from the base class. Note: This is an SPI class, not intended to be used by applications.
publicfinalTransactionStatusgetTransaction(@NullableTransactionDefinition definition) throws TransactionException {// ......// Check definition settings for new transaction.if (definition.getTimeout() <TransactionDefinition.TIMEOUT_DEFAULT) {thrownewInvalidTimeoutException("Invalid transaction timeout",definition.getTimeout());}// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_MANDATORY) {thrownewIllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}elseif (definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_REQUIRED||definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_REQUIRES_NEW||definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_NESTED) {// ......}
publicstaticObjectunbindResource(Object key) throws IllegalStateException {Object actualKey =TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Object value =doUnbindResource(actualKey);if (value ==null) {thrownewIllegalStateException("No value for key ["+ actualKey +"] bound to thread ["+Thread.currentThread().getName() +"]"); }return value;}
又看到了doXXX,进到 doUnbindResource 中:
3.3.4.1.3 doUnbindResource
privatestaticfinalThreadLocal<Map<Object,Object>> resources =newNamedThreadLocal<>("Transactional resources");privatestaticObjectdoUnbindResource(Object actualKey) {Map<Object,Object> map =resources.get();if (map ==null) {returnnull;}Object value =map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}// Transparently suppress a ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { value =null;}if (value !=null&&logger.isTraceEnabled()) {logger.trace("Removed value ["+ value +"] for key ["+ actualKey +"] from thread ["+Thread.currentThread().getName() +"]");}return value;}
protectedvoiddoBegin(Object transaction,TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con =null;try {// 如果当前线程中没有ConnectionHolder,则会获取新的数据库连接,并放入ConnectionHolder(线程绑定)if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {// 从DataSource中取connectionConnection newCon =obtainDataSource().getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection ["+ newCon +"] for JDBC transaction"); }txObject.setConnectionHolder(newConnectionHolder(newCon),true); }txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con =txObject.getConnectionHolder().getConnection();Integer previousIsolationLevel =DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already).if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection ["+ con +"] to manual commit"); }// 开启事务con.setAutoCommit(false); }prepareTransactionalConnection(con, definition);// 标记事务状态为激活txObject.getConnectionHolder().setTransactionActive(true);int timeout =determineTimeout(definition);if (timeout !=TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }// Bind the connection holder to the thread.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); }}
protectedTransactionInfoprepareTransactionInfo(@NullablePlatformTransactionManager tm, @NullableTransactionAttribute txAttr,String joinpointIdentification, @NullableTransactionStatus status) {TransactionInfo txInfo =newTransactionInfo(tm, txAttr, joinpointIdentification);if (txAttr !=null) {// We need a transaction for this method...if (logger.isTraceEnabled()) {logger.trace("Getting transaction for ["+txInfo.getJoinpointIdentification() +"]"); }// The transaction manager will flag an error if an incompatible tx already exists.txInfo.newTransactionStatus(status); }else {// The TransactionInfo.hasTransaction() method will return false. We created it only// to preserve the integrity of the ThreadLocal stack maintained in this class.if (logger.isTraceEnabled()) {logger.trace("No need to create transaction for ["+ joinpointIdentification +"]: This method is not transactional."); } }// We always bind the TransactionInfo to the thread, even if we didn't create// a new transaction here. This guarantees that the TransactionInfo stack// will be managed correctly even if no transaction was created by this aspect.txInfo.bindToThread();return txInfo;}
中间大段的日志打印就不看了,最后有一个 txInfo.bindToThread() :
3.3.7 txInfo.bindToThread()
privatestaticfinalThreadLocal<TransactionInfo> transactionInfoHolder =newNamedThreadLocal<>("Current aspect-driven transaction");privatevoidbindToThread() {// Expose current TransactionStatus, preserving any existing TransactionStatus// for restoration after this transaction is complete.this.oldTransactionInfo=transactionInfoHolder.get();transactionInfoHolder.set(this);}
publicfinalTransactionStatusgetTransaction(@NullableTransactionDefinition definition) throws TransactionException {Object transaction =doGetTransaction();// ......if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.returnhandleExistingTransaction(definition, transaction, debugEnabled);}
privateTransactionStatushandleExistingTransaction(TransactionDefinition definition,Object transaction,boolean debugEnabled) throws TransactionException {// ......// 如果当前方法的事务行为是NESTED,创建一个保存点if (definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_NESTED) {if (!isNestedTransactionAllowed()) {thrownewNestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - "+"specify 'nestedTransactionAllowed' property with value 'true'"); }if (debugEnabled) {logger.debug("Creating nested transaction with name ["+definition.getName() +"]"); }if (useSavepointForNestedTransaction()) {// Create savepoint within existing Spring-managed transaction,// through the SavepointManager API implemented by TransactionStatus.// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction,false,false, debugEnabled,null);status.createAndHoldSavepoint();return status; }else {// Nested transaction through nested begin and commit/rollback calls.// Usually only for JTA: Spring synchronization might get activated here// in case of a pre-existing JTA transaction.boolean newSynchronization = (getTransactionSynchronization()!= SYNCHRONIZATION_NEVER);DefaultTransactionStatus status =newTransactionStatus( definition, transaction,true, newSynchronization, debugEnabled,null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status; } }// ......returnprepareTransactionStatus(definition, transaction,false, newSynchronization, debugEnabled,null);}
protectedSavepointManagergetSavepointManager() {Object transaction =this.transaction;if (!(transaction instanceof SavepointManager)) {thrownewNestedTransactionNotSupportedException("Transaction object ["+this.transaction+"] does not support savepoints"); }return (SavepointManager) transaction;}
很简单,它只是把当前的事务做了一次强转。
3.6.3.2 createSavepoint
publicObjectcreateSavepoint() throws TransactionException {ConnectionHolder conHolder =getConnectionHolderForSavepoint();try {if (!conHolder.supportsSavepoints()) {thrownewNestedTransactionNotSupportedException("Cannot create a nested transaction because savepoints are not supported by your JDBC driver"); }if (conHolder.isRollbackOnly()) {thrownewCannotCreateTransactionException("Cannot create savepoint for transaction which is already marked as rollback-only"); }returnconHolder.createSavepoint(); }catch (SQLException ex) {thrownewCannotCreateTransactionException("Could not create JDBC savepoint", ex); }}