假如有个TestController类,类内注入TestServiceTestService有个方法test()上开启了事务。

@RestController
public class TestController {

    @Autowired
    private TestService testService;

    public void test() {
        testService.test();
    }

}

TestController类内注入TestService实际上是个代理类TestServiceImpl$$EnhancerBySpringCGLIB$$518FB1FA@11018,springboot事务默认是用CGLIB方式代理。
当执行testService.test()时,进入到CglibAopProxy类里的内部类DynamicAdvisedInterceptor下的intercept方法。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Object target = null;
   TargetSource targetSource = this.advised.getTargetSource();
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 没有拦截器
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else { // 拦截器
         // We need to create a method invocation...
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

可以看到,代理类会检查当前调用的方法是否有拦截器List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);。如果没有(方法上没有开启事务),就会直接调用原始方法 methodProxy.invoke(target, argsToUse)。如果有,则执行CglibMethodInvocation.proceed()方法。这时候就是平时常见的AOP-Chain链模式了。

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

    @Override
    @Nullable
    public Object proceed() throws Throwable {
       // We start with an index of -1 and increment early.
       if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
          return invokeJoinpoint();
       }

       Object interceptorOrInterceptionAdvice =
             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
       if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
          // Evaluate dynamic method matcher here: static part will already have
          // been evaluated and found to match.
          InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
          Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
          if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
             return dm.interceptor.invoke(this);
          }
          else {
             // Dynamic matching failed.
             // Skip this interceptor and invoke the next in the chain.
             return proceed();
          }
       }
       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);
       }
    }
}

事务的代码在TransactionAspectSupport中,主要流程就是在实际方法执行前开启事务,执行之后提交/回滚事务。:

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    @Override
    @Nullable
    public Object invoke(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);

    }
    /**
     * 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;
          }
       }
    }

}

46924898.png
以上是实际方法debug流程图,可以事务整体流程如下:

- proxy.update 
--- 获取匹配aop增强列表
--- 链式调用aop的前置方法
----- target.update
--- 链式调用aop的后置方法

常见的类内方法调用,事务不生效的问题:

public class BserviceImpl implements Bservice {

    public void testA() {
        this.testB();
    }

   @Transactional(rollbackFor = Exception.class) 
   public void testB() {
        
    }

}

是因为其他类调用testA时,因为testA没有事务注解,所以proxy获取的拦截器chain没有事务拦截器,就不会开启事务。而testA调用testB时,由于是target类内部调用,并没有走proxy,所以也不会开启事务。
如果一定要让testA调用testB时,开启事务,可以使用spring的expose-proxy特性,将expose-proxy设为true

xml:
<aop:aspectj-autoproxy expose-proxy=“true”> 

注解:
@EnableAspectJAutoProxy(exposeProxy=true)

然后将this.testB()改为((Bservice)AopContext.currentProxy()).testB(),开启expose-proxy后,spring会将当前代理类放入ThreadLocal中AopContext.setCurrentProxy(proxy)

另:
Spring/JDBC的事务(获取活跃事务列表trx_list)是从第一个SQL执行开始的。

@Transactional(isolation = Isolation.REPEATABLE\_READ)
public void test() {
    testMapper.testSelect(); // 第一步,随便查询个表
    List<VendorAccount> vendorAccountList = vendorAccountMapper.listAll(); // 第二步,查询vendor_account表,跟第一步的表不是同一个表。
    System.out.println(vendorAccountList);
}
  1. debug断点打在第一步,执行第一步之前,修改vendor_account表(第一次修改)。
  2. debug断点进行到第二步,在执行第二步之前,修改vendor_account表(第二次修改)。
  3. 执行第二步,根据获取到的vendorAccountList内容,可以看到,vendorAccountList取到了第一次修改,但是没有取到第二次修改。

noname
314 声望49 粉丝

一只菜狗