Today, I went to the dentist and he asked me why my grade teeth were so badly worn. I mean, I’ve been grinding my teeth through all these years without likes.

Brief introduction to the @Transactional annotation

Transactional annotation @Transactional is the spring annotation configuration for declarative transaction management. The @Transactional annotation helps manage transactions that are opened, committed, or rolled back using AOP.

The @Transactional annotation lets Spring manage transactions for us, eliminating repetitive transaction management logic, reducing intrusion into business code, and allowing us developers to focus on business development.

We know that the @Transactional principle is based on Spring AOP, which implements the dynamic proxy pattern. By reading the source code, we have outlined the following steps to understand how Spring uses AOP to implement @Transactional functionality in practice.

Conjecture on how declarative transactions work in Spring

First, if you’re familiar with aop implementations in Spring, you should know that to proxy a method, you definitely need to define pointcuts. In the @Transactional implementation, as well, Spring defines pointcuts with the @Transactional annotation as the insertion point so that methods annotated by the @Transactional annotation need to be propped.

Once you have the aspect definition, during the initialization of spring’s bean, you need to proxy the instantiated bean and generate the proxy object.

The @Transactional annotation implements proxy-like logic when making method calls. The @transactional annotation implements proxy-like logic in Spring.

@ Transactional role

Based on the above theoretical conjecture, the following is a brief introduction to the source code of each step for verification.

The first is @Transactional, which defines proxy placement points. We know that the proxy objects created by BeanPostProcessor implementation class AnnotationAwareAspectJAutoProxyCreator postProcessAfterInstantiation method to implement a, if the need for agents, This method will return a proxy object to the container and determine the insertion point in this method.

The following analysis, after configured annotations drive mode of transaction management, spring will create a BeanFactoryTransactionAttributeSourceAdvisor instance in the ioc container, the instance can be thought of as a point of contact, In judging whether a bean in the initialization process need to create a proxy object, all need to verify whether a BeanFactoryTransactionAttributeSourceAdvisor applicable point of tangency of the bean. If it is, you need to create a proxy object, and the BeanFactoryTransactionAttributeSourceAdvisor instance into proxy objects.

The call stack can be broken at AopUtils#findAdvisorsThatCanApply to determine whether the slice applies to the current bean. AopUtils#findAdvisorsThatCanApply is called consistently, Finally, the following code is used to determine whether the pointcut is applicable.

  • AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<? > targetClass)Here can be based on the parameters of the conditional breakpoint to debug analysis call stack, targetClass is the targetClass… Series of calls
  • In the endSpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    / / here is whether analysis Method be @ Transactional annotations, so, needless to say BeanFactoryTransactionAttributeSourceAdvisor fit the current bean, agent, and the injection point of tangency
    //BeanFactoryTransactionAttributeSourceAdvisor
   AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
   if(attributes ! =null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null; }}Copy the code

This is how you decide if you need to create a proxy object under @Transactional. The Transactional function of @Transactional is to identify methods that need to be broached, and to carry information about attributes needed for transaction management.

Dynamic proxy logic implementation

【 AOP implementation principle analysis 】 know that THE ultimate AOP proxy object proxy method is

  • DynamicAdvisedInterceptor#intercept

So we can analyze the agent logic at this method break point.

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false; Class<? > targetClass =null;
   Object target = null;
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
      target = getTarget();
      if(target ! =null) {
         targetClass = target.getClass();
      }
       //follow
      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) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.AopContext.setCurrentProxy(oldProxy); }}}Copy the code

Through the analysis of the List < Object > chain = this. Advised. GetInterceptorsAndDynamicInterceptionAdvice (method, TargetClass) returns a TransactionInterceptor. How to use a TransactionInterceptor to implement a proxy logic call?

MethodInvocation(Proxy, Target, Method, ARgs, targetClass, chain, methodProxy).proceed();

It turns out that the TransactionInterceptor#invoke method is called and the CglibMethodInvocation is injected into the invoke method. The CglibMethodInvocation wraps all the necessary information for the target method invocation. Therefore, the TransactionInterceptor#invoke method invocation can also invoke the target method and implement @around logic. Continue to inject some additional logic, such as transaction management logic, before and after the target method invocation.

Pay attention to the public number [boring learning Java] to get the latest dry goods video

TransactionInterceptor – The ultimate transaction manager

Let’s look at the code.

  • TransactionInterceptor#invoke
@Override
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, new InvocationCallback() {
  @Override
  public Object proceedWithInvocation(a) throws Throwable {
   returninvocation.proceed(); }}); }Copy the code

Following up on invokeWithinTransaction, you can actually see some logic in the code below, which is what we suspect to be implemented, transaction management.

protected Object invokeWithinTransaction(Method method, Class<? > targetClass,final InvocationCallback invocation)
      throws Throwable {

   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);

   if (txAttr == null| |! (tminstanceof CallbackPreferringPlatformTransactionManager)) {
      // Standard transaction demarcation with getTransaction and commit/rollback calls.
       // Start the transaction
      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.
          // method call
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // target invocation exception
     // Rollback the transaction
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
       // Commit the transaction
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }

   else {
      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
               new TransactionCallback<Object>() {
                  @Override
                  public Object doInTransaction(TransactionStatus 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 newThrowableHolderException(ex); }}else {
                           // A normal return value: will lead to a commit.
                           return newThrowableHolder(ex); }}finally{ cleanupTransactionInfo(txInfo); }}});// Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            returnresult; }}catch (ThrowableHolderException ex) {
         throwex.getCause(); }}}Copy the code

conclusion

Finally, you can summarize the process and compare it to your initial guess.

After analyzing the source code