Две стороны Meituan: как реализован @Transactional в Spring?

Java

Ходила сегодня к стоматологу, и он спросил меня, почему мои светлые зубы так сильно изношены? Я сказал, что приходил сюда со стиснутыми зубами в те годы, когда не поднимал большого пальца вверх.

Введение в аннотацию @Transactional

@TransactionalЭто метод настройки аннотаций для декларативного управления транзакциями в spring Я думаю, что все знают роль этой аннотации.@TransactionalАннотации могут помочь нам управлять операциями открытия, фиксации или отката транзакций через aop.

пройти через@TransactionalАннотации позволяют Spring управлять транзакциями за нас, устраняя повторяющуюся логику управления транзакциями, уменьшая вмешательство в бизнес-код и позволяя нашим разработчикам сосредоточиться на разработке на уровне бизнеса.

img

Мы знаем, что для достижения принципа @transactional основана на Spring AOP, AOP - это модель динамического агентства, посредством чтения исходного кода, суммировавшими следующие шаги, чтобы понять реальность, как использование весной AOP для достижения @transactional функций Отказ

Гипотеза о принципе реализации декларативной транзакции в spring

Прежде всего, если у вас есть понимание принципа реализации aop в spring, вы должны знать, что если вы хотите проксировать метод, вы должны определить pointcut. В реализации @Transactional то же самое, Spring определяет для нас pointcut с аннотацией @Transactional в качестве точки имплантации, чтобы мы могли знать, что метод, аннотированный аннотацией @Transactional, должен быть проксирован.

С определением аспекта в процессе инициализации bean-компонента Spring необходимо проксировать созданный bean-компонент и создать прокси-объект.

В прокси-логике для генерации прокси-объектов при вызове метода необходимо сначала получить логику аспекта.Аспектная логика аннотации @Transactional аналогична @Around, которая реализует аналогичную логику прокси в spring.

img

@Транзакционная роль

Согласно вышеуказанному принципу гипотезе, следующее представляет собой краткое введение в исходный код каждого шага для проверки.

Первый — это @Transactional, определяющий точку имплантации прокси. Мы знаем, что прокси-объект создаетсяBeanPostProcessorкласс реализацииAnnotationAwareAspectJAutoProxyCreatorизpostProcessAfterInstantiationметод для достижения этого, если требуется проксирование, то этот метод вернет прокси-объект в контейнер, и точка имплантации суждения также находится в этом методе.

Затем начните анализ ниже.После настройки управления транзакциями на основе аннотаций Spring создастBeanFactoryTransactionAttributeSourceAdvisorНапример, этот экземпляр можно рассматривать как pointcut.При оценке необходимости создания bean-компонентом прокси-объекта в процессе инициализации его необходимо проверить один раз.BeanFactoryTransactionAttributeSourceAdvisorЭто PointCut для этого боба. Если это так, вам нужно создать прокси-объект и поставитьBeanFactoryTransactionAttributeSourceAdvisorЭкземпляр внедряется в прокси-объект.

Мы знаем до этогоAopUtils#findAdvisorsThatCanApplyЧтобы определить, применим ли аспект к текущему компоненту, вы можете поставить точку останова и проанализировать стек вызовов в этом месте.AopUtils#findAdvisorsThatCanApplyПоследовательно вызовите и, наконец, оцените, применим ли pointcut, с помощью следующего кода.

  • AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass)Здесь можно ставить условные точки останова на параметры для отладки и анализа стека вызовов, targetClass — целевой класс… серия вызовов
  • наконец-тоSpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    //这里就是分析Method是否被@Transactional注解标注,有的话,不用说BeanFactoryTransactionAttributeSourceAdvisor适配当前bean,进行代理,并且注入切点
    //BeanFactoryTransactionAttributeSourceAdvisor
   AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

Выше приведен процесс оценки необходимости создания прокси-объекта в соответствии с @Transactional. Одна из функций @Transactional состоит в том, чтобы определить, что метод должен быть проксирован, а другая — в том, чтобы нести некоторую атрибутивную информацию, необходимую для управления транзакциями.

Реализация логики динамического прокси

[Анализ принципа реализации аоп], мы знаем, что прокси-метод конечного прокси-объекта аоп

  • DynamicAdvisedInterceptor#intercept

Таким образом, мы можем проанализировать логику прокси в этой точке останова метода.

@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);
      }
   }
}

после анализаList<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)Возвращает TransactionInterceptor, как добиться логических вызовов прокси-сервера TransactionInterceptor?

отслеживатьnew CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

найти, что последний вызовTransactionInterceptor#invokeметод и внедрить CglibMethodInvocation в метод вызова, как вы можете видеть из приведенного вышеCglibMethodInvocationУпаковка всей необходимой информации Объект 'вызывает, следовательно,TransactionInterceptor#invokeЦелевой метод также может вызываться внутри, также может быть реализована логика, аналогичная @Around, и может продолжаться внедрение некоторой другой логики, например логики управления транзакциями, до и после вызова целевого метода.

Обратите внимание на общедоступный номер [Скучно изучать Java], чтобы получать последние видео о галантерейных товарах.

TransactionInterceptor — лучший менеджер транзакций

См. код ниже.

  • 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() throws Throwable {
   return invocation.proceed();
  }
 });
}

Продолжайте отслеживать invokeWithinTransaction.На самом деле, некоторые логические подсказки можно увидеть в следующем коде, который является методом реализации нашего предположения, управления транзакциями.

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 || !(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 {
      // 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 new ThrowableHolderException(ex);
                           }
                        }
                        else {
                           // A normal return value: will lead to a commit.
                           return new ThrowableHolder(ex);
                        }
                     }
                     finally {
                        cleanupTransactionInfo(txInfo);
                     }
                  }
               });

         // Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            return result;
         }
      }
      catch (ThrowableHolderException ex) {
         throw ex.getCause();
      }
   }
}

Суммировать

Наконец, вы можете обобщить весь процесс и сравнить его с первоначальным предположением.

img

После анализа исходного кода

img