Spring Framework version 5.3.x

1. The relationship between core objects

As shown above can see comments @ EnableTransactionManagement is responsible for the open transaction management, and then by TransactionManagementConfigurationSelector injected into the following two categories:

  • AutoProxyRegistrar

    Responsible for creating proxy classes

  • ProxyTransactionManagementConfiguration

    Logic responsible for creating transaction management (Spring AOP)

    • BeanFactoryTransactionAttributeSourceAdvisor

      Advisors in Spring AOP

    • TransactionAttributeSource

      Pointcut in Spring AOP

    • TransactionInterceptor

      Advice in Spring AOP

This shows that transaction management is really an application implementation of Spring AOP.

2. Annotate the transaction flow

Spring annotation Transaction management running flowchart:

3. Source code analysis

The source code is analyzed from three components of transaction management AOP, which are as follows:

  • [TransactionAttributeSource (Poincut)] (# 3.1 TransactionAttributeSource source analysis)
  • [TransactionInterceptor(Advice)](#3.2 TransactionInterceptor)
  • BeanFactoryTransactionAttributeSourceAdvisor(Advisor)

3.1 TransactionAttributeSource source code parsing

public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
		implements Serializable {
    
    private final boolean publicMethodsOnly;
    
	// omit the code
    public AnnotationTransactionAttributeSource(a) {
		this(true);
	}
    
    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
		this.publicMethodsOnly = publicMethodsOnly;
		if (jta12Present || ejb3Present) {
			this.annotationParsers = new LinkedHashSet<>(4);
			this.annotationParsers.add(new SpringTransactionAnnotationParser());
			if (jta12Present) {
				this.annotationParsers.add(new JtaTransactionAnnotationParser());
			}
			if (ejb3Present) {
				this.annotationParsers.add(newEjb3TransactionAnnotationParser()); }}else {
			this.annotationParsers = Collections.singleton(newSpringTransactionAnnotationParser()); }}}Copy the code

View AnnotationTransactionAttributeSource created ProxyTransactionManagementConfiguration is to use no arguments constructor.

Tips: this call is AnnotationTransactionAttributeSource (Boolean publicMethodsOnly) constructor. In this case, transaction management can only handle public methods

The inside is the most important class SpringTransactionAnnotationParser annotation class, the other two other specification is designed to support Java. Main function is used to determine whether the currently executing method or class contains @ javax.mail. Transaction. Transactional, @ javax.mail. Ejb. TransactionAttribute, @ org. Springframework. Transaction. The annotation. Transactional annotation.

See here will be asked not to say that good TransactionAttributeSource equivalent of Pointcut useless also see Pointcut interface is realized. Don’t look at the below ProxyTransactionManagementConfiguration class is such a piece of code:

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource);
		advisor.setAdvice(transactionInterceptor);
		if (this.enableTx ! =null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}
Copy the code

Sets the TransactionAttributeSource’s implementation class for BeanFactoryTransactionAttributeSourceAdvisor an attribute. (here is actually AnnotationTransactionAttributeSource instances). Look at the below BeanFactoryTransactionAttributeSourceAdvisor class there is such a piece of code:

	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource(a) {
			returntransactionAttributeSource; }};Copy the code

This becomes a Pointcut. Instead of directly inheriting the Pointcut interface, it becomes Poincut indirectly through the code above. Look at the code TransactionAttributeSourcePointcut * * * *

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
	// omit the code
}
Copy the code

Through the packaging AnnotationTransactionAttributeSource into a Pointcut

3.2 TransactionInterceptor source code parsing

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor.Serializable {
    // omit the code
}
Copy the code

As you can see from above, the interface MethodInterceptor is implemented, so the main logic is in the MethodInterceptor#invoke method. The TransactionInterceptor implements the invoke interface:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
	// Get the target classClass<? > targetClass = (invocation.getThis() ! =null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
		@Override
		@Nullable
		public Object proceedWithInvocation(a) throws Throwable {
			return invocation.proceed();
		}
		@Override
		public Object getTarget(a) {
			return invocation.getThis();
		}
		@Override
		public Object[] getArguments() {
			returninvocation.getArguments(); }}); }Copy the code

Find the target class for the call, and then invoke the invokeWithinTransaction method of the TransactionAspectSupport class. This method is also the main processing logic:

To decide to use according to TransactionAttributeSource ReactiveTransactionManager PlatformTransactionManager (here only analysis PlatformTransactionManager).

TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Copy the code

According to the PlatformTransactionManager TransactionAttributeSource and how to create the entry point method to deal with affairs management, Look at the below TransactionAspectSupport# createTransactionIfNecessary method, there is a piece of code:

	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
		// Omit some code
		TransactionStatus status = null;
		if(txAttr ! =null) {
			if(tm ! =null) {
                // This code
				status = tm.getTransaction(txAttr);
			}
			else{}}return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}
Copy the code

The transaction manager according to TransactionAttributeSource TransactionStatus to transaction state.

Status = tm.getTransaction(txAttr) This code shows how to create a transaction based on the @Transactional attribute, preferably wrapped as TransactionStatus. To analyze the code below, by AbstractPlatformTransactionManager getTransaction implements the method.

	@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {

		// Use the default if no transaction definition is givenTransactionDefinition def = (definition ! =null ? definition : TransactionDefinition.withDefaults());

        // Get transaction
		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();
		
        // Determine whether the transaction exists
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}

		// Check the new transaction configuration
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]." + def);
			}
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throwex; }}else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if(def.getIsolationLevel() ! = TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null.true, newSynchronization, debugEnabled, null); }}Copy the code

The transaction is handled based on the propagation behavior of the set transaction. The propagation behavior of transactions is as follows:

  • Transactional(Propagation =Propagation.REQUIRED) : Join a transaction if one exists, create a new one if not (default)

  • @Transactional(Propagation = Propagation.not_supported) : Executes in a non-transactional manner, suspending the current transaction if one exists

  • Transactional(Propagation =Propagation.REQUIRES_NEW) : Creates a new transaction regardless of whether it exists

  • Transactional(Propagation = Propagation.Mandatory) : Must be executed within an existing transaction, otherwise an exception is raised

  • Transactional(Propagation =Propagation.NEVER) : Executes in a non-transactional manner, throwing an exception if a transaction exists.

  • Transactional(Propagation =Propagation.SUPPORTS) : If other beans call this method to declare transactions in other beans, use transactions. If the other bean does not declare a transaction, then no transaction is required.

  • Transactional(Propagation =Propagation.NESTED) : Execute within a NESTED transaction if a transaction currently exists. If there are no transactions currently, an operation like PROPAGATION_REQUIRED is performed.

After TransactionInfo is created, execute the business logic method. If there is an error executing the business logic, execute:

completeTransactionAfterThrowing(txInfo, ex);
Copy the code

Then execute in the finally code block:

cleanupTransactionInfo(txInfo);
Copy the code

Clear transaction information and commit the transaction. At this point the whole transaction is basically done.

3.3 BeanFactoryTransactionAttributeSourceAdvisor source code parsing

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

	@Nullable
	private TransactionAttributeSource transactionAttributeSource;

	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		@Nullable
		protected TransactionAttributeSource getTransactionAttributeSource(a) {
			returntransactionAttributeSource; }};/**
	 * Set the transaction attribute source which is used to find transaction
	 * attributes. This should usually be identical to the source reference
	 * set on the transaction interceptor itself.
	 * @see TransactionInterceptor#setTransactionAttributeSource
	 */
	public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
		this.transactionAttributeSource = transactionAttributeSource;
	}

	/**
	 * Set the {@link ClassFilter} to use for this pointcut.
	 * Default is {@link ClassFilter#TRUE}.
	 */
	public void setClassFilter(ClassFilter classFilter) {
		this.pointcut.setClassFilter(classFilter);
	}

	@Override
	public Pointcut getPointcut(a) {
		return this.pointcut; }}Copy the code

This class simply assembles Advice and Pointcut into advisors

4. To summarize

Spring transaction management is implemented using AOP principles at the bottom. Combined with database transactions.