Как работает транзакция

Spring

Обзор

@Transactional — это аннотация, используемая управлением транзакциями Spring.Если эту аннотацию добавить к методу, то метод будет транзакционным, а операции в методе будут либо зафиксированы, либо откатываться вместе; о работе, стоящей за этим Принцип включает много знаний Целью этой статьи является разобраться во всем процессе Более конкретные свойства конфигурации транзакций и интеграция транзакций Spring и сторонних платформ ORM не являются предметом этой статьи;

Используйте @Transactional

Включите поддержку аннотации @Transactional в файле конфигурации application-context.xml, настройте путь сканирования и т. д.

<tx:annotation-driven />

Аннотировать метод службы

@Service
public class UserServiceImpl implements UserService {
    @Transactional
    public int register(User user) {
        addUser(user);
        auditLog(user);
    }
}

Выше приведен простой способ настройки и использования, чтобы @Transactional работал нормально;

Принципиальный анализ

То, что делает @Transactional, примерно так:

try { 
    tx.begin(); 
    businessLogic();
    tx.commit(); 
} catch(Exception ex) { 
    tx.rollback(); 
    throw ex; 
}

Это также нормальная работа диспетчера транзакций TransactionManager, но здесь аннотация используется для «завершения» способа ручного написания кода управления транзакциями; все другие методы службы, которым необходимо открывать транзакции, настраиваются только с такой аннотацией, которая может сохранить способ написания кода, это идея аспектного программирования, и она также дополнена Spring AOP в сочетании с аннотациями; для приведенной выше конфигурации xml и кода дополнительно проанализируйте, как Spring наконец реализует этот аспект;

BeanDefinition and BeanPostProcessor

Во-первых, это поддержка пространства имен tx,TxNamespaceHandlerзарегистрирован вAnnotationDrivenBeanDefinitionParserParser, который обеспечивает поддержку разбора свойств на основе аннотаций; здесь создаются три важных BeanDefinition и BeanPostProcessor, а именно BeanDefinition,

  • TransactionAttributeSource // используется для получения атрибутов аннотации @Transactional
  • TransactionInterceptor // Перехватчик метода зависит от TransactionManager Bean; зависит от transactionAttributeSource

  • TransactionAttributeSourceAdvisor // Зависит от transactionAttributeSource; Зависит от TransactionInterceptor; TransactionAttributeSourceAdvisor — это PointcutAdvisor, который является содержимым Spring AOP, включая pointCut и совет BeanPostProcessor,
  • InfrastructureAdvisorAutoProxyCreator // используется для создания прокси

postProcessAfterInitialization

Конечно, когда bean-компонент зависит от bean-компонента UserService, ему сначала необходимо инициализировать UserServiceImpl и применить постпроцессор.При применении постпроцессора InfrastructureAdvisorAutoProxyCreator обнаруживается, что UserServiceImpl использует аннотацию @Transaction, поэтому его следует проксировать, то есть здесь создается прокси, а экземпляр прокси возвращается в Бин, который зависит от UserService; анализируем процесс создания прокси,

// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
}

Получите перехватчик метода TransactionInterceptor перед созданием прокси, а затем передайте его методу createProxy для завершения; процесс в createProxy является процессом Spring AOP, перехватчик метода организован в Advisor, а InvocationHandler создается в зависимости от того, используется интерфейс прокси.InvocationHandler здесь JdkDynamicAopProxy, и, наконец, он используется для создания экземпляра прокси,

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

invoke on proxy instance

Когда вызывается метод службы, фактически вызывается метод класса прокси.Экземпляр прокси, созданный JdkDynamicAopProxy в качестве InvocationHandler, в конечном итоге будет вызываться для метода вызова JdkDynamicAopProxy (Примечание: динамический прокси JDK), и метод вызова будет использовать предыдущий TransactionInterceptor,

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();

ReflectiveMethodInvocation в конечном итоге вызовет метод вызова TransactionInterceptor.

TransactionInterceptor

Вызов TransactionInterceptor может найти следы управления транзакциями, для выполнения такой операции он вызывает invokeWithinTransaction родительского класса.

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

tm — это TransactionManager, от которого зависит TransactionInterceptor на этапе регистрации BeanDefinition; здесь он не показан.

tx.begin()
tx.commit()
tx.rollback()

Подождите, на самом деле все эти методы, связанные с транзакциями, выполняются TransactionManager, отслеживая вышеуказанное.createTransactionIfNecessaryметод, вы обнаружите, что операция doBegin будет выполняться в методе getTransaction диспетчера транзакций.Метод doBegin создаст соединение с базой данных из источника данных диспетчера транзакций и, наконец, свяжет источник данных с соединением и привяжет его к текущему поток, то есть он существует в ThreadLocal,

TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());

commitTransactionAfterReturningЭто эквивалентноtx.commit,completeTransactionAfterThrowingэквивалентноtx.rollback;

TransactionInterceptor vs TransactionManager vs JdbcTemplate vs TransactionSynchronizationManager

Это было разобрано здесь. Spring использует BeanPostProcessor для прокси-классов, аннотированных с помощью @Transactional, а InvocationHandler, созданный BeanPostProcessor, использует перехватчик метода TransactionInterceptor. В этом перехватчике метода реализована операция управления транзакциями. Операция для достижения управления транзакциями Это делается диспетчером транзакций TransactionInterceptor, а диспетчер транзакций использует источник данных для создания ссылки на базу данных;

Однако остается проблема, как отдать Connection, созданный TransactionInterceptorbusinessLogic, даваяUserServiceImpl#registerиспользуется; здесь мы используемJdbcTemplateВ качестве примера для анализа,JdbcTemplateОперация должна получить соединение с базой данных Connection, которое реализовано следующим образом:

Connection con = DataSourceUtils.getConnection(this.obtainDataSource());

Отслеживая код DataSourceUtils, вы в конечном итоге найдете такой код

ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);

Это соответствует предыдущим привязкам, привязка к ThreadLocal, а получение из ThreadLocal связано с TransactionSynchronizationManager, на самом деле mybatis использует управление транзакциями Spring, и его способ получения ссылок также использует DataSourceUtils;