From: www.jianshu.com/p/ae2bff8cb…

1. Lead

While the previous section looked at the steps Spring takes to implement transaction management, this article looks at the process of creating Spring transactions.

2. A brief analysis of the method of creating things

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. // 1. If no name is specified, the method id is applied as the transaction name.if(txAttr ! = null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public StringgetName() {
                returnjoinpointIdentification; }}; TransactionStatus status = null;if(txAttr ! = null) {if(tm ! = null) {// Key: returns the currently active transaction or creates a new transaction, depending on the specified propagation behavior. status = tm.getTransaction(txAttr); }else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured"); }}} // 3. Create TransactionInfo objectreturn prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
Copy the code

The most important is the process of getting the TransactionStatus object and creating the TransactionInfo object, which are described separately below.

3. Get the TransactionStatus object, the Spring transaction creation process

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { / / 1. Get the current transaction Object (if it already exists) Object Transaction =doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); / / if the TransactionDefinition is empty, create DefaultTransactionDefinition object by defaultif (definition == null) {
        // Use defaults ifno transaction definition given. definition = new DefaultTransactionDefinition(); } // 2. If there are already things // Key: // If there are already things to start, it will evaluate the propagation characteristics of the thing to be created this time to determine the subsequent processing of the new thingif (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        returnhandleExistingTransaction(definition, transaction, debugEnabled); } // Check definition SettingsforThe new transaction. / / 3.1 if things defined timeout, less than the default timeout, an exception is thrown, TransactionDefinition. TIMEOUT_DEFAULT -- -- > 1if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> Check Propagation behavior to find out how to proceed Throw an exception if the current thing is PROPAGATION_MANDATORY (because the current thing has not been created and opened...) // PROPAGATION_MANDATORY --> Uses the current thing, and throws an exception if there is none.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); } // create a new thing if there are three propagating attributes: // PROPAGATION_REQUIRED --> If there is no current thing, create a new thing; If one already exists, add to it. // PROPAGATION_REQUIRES_NEW --> Create something, and suspend it if it already exists. Execute within nested things if something currently exists If nothing is currently present, it is the same as PROPAGATION_REQUIREDelse if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED
            || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW
            || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if(debugEnabled) {// From the log print, you can also see that a new thing called megary.getName () is currently being created... logger.debug("Creating new transaction with name [" + definition.getName() + "]."+ definition); } try { boolean newSynchronization = (getTransactionSynchronization() ! = SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus(definition, transaction,true, newSynchronization, debugEnabled, suspendedResources); // Open things updoBegin(transaction, definition); // Initialize transaction synchronization. prepareSynchronization(status, definition);returnstatus; } catch (RuntimeException | Error ex) { resume(null, suspendedResources); throw ex; }} // 3.4 For the other three propagation features, no new things need to be opened // PROPAGATION_SUPPORTS --> Supports the current thing, if there is no current thing, Execute in a non-thing manner // PROPAGATION_NOT_SUPPORTED --> Execute in a non-thing manner, suspend the current thing if it currently exists // PROPAGATION_NEVER --> Execute in a non-thing manner, throw an exception if it currently existselse {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        if(definition.getIsolationLevel() ! = TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; "
                    + "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(
                definition,
                null,
                true, newSynchronization, debugEnabled, null); }}Copy the code

This approach is arguably the most critical and core approach to Spring things. For the more important methods here, analyze one by one…

3.1 Getting the current thing object (if the current thing already exists)

Manager for different things, get method is different, the example USES DataSourceTransactionManager

protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); . / / whether to allow use of savepoint txObject setSavepointAllowed (isNestedTransactionAllowed ()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder,false);
    return txObject;
}
Copy the code

3.2 If something already exists

Here, involving nesting of things, left to analyze separately…

3.3 If nothing currently exists

Here will not involve the nested processing of things, analysis is also relatively simple, the next processing is to configure the propagation characteristics of things, to analyze one by one

3.3.1 Handling timeout period

Quite simply, if the custom timeout is less than the default timeout, an exception is thrown

3.3.2 Handling PROPAGATION_MANDATORY

If we define an object propagation attribute as PROPAGATION_MANDATORY, an exception is thrown because there is no object currently, or it is still being created. PROPAGATION_MANDATORY is to use the current thing, and throw an exception if there is none.

3.3.3 Handling propagating features PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW, and PROPAGATION_NESTED

Let’s review the three propagation features: PROPAGATION_REQUIRED –> Create a new thing if there is no current one; If one already exists, add to it. PROPAGATION_REQUIRES_NEW –> Create something, and suspend it if it already exists. Execute within nested things if something currently exists, PROPAGATION_NESTED –> If nothing is currently present, it is the same as PROPAGATION_REQUIRED

Because there is currently no object, we need to create a new object based on these features, snippet:

try { boolean newSynchronization = (getTransactionSynchronization() ! = SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus(definition, transaction,true, newSynchronization, debugEnabled, suspendedResources); // Open things updoBegin(transaction, definition); // Initialize transaction synchronization. prepareSynchronization(status, definition);return status;
}
Copy the code

Small branches too much, the following alone in open an introduction…

3.3.4 Handle PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, and PROPAGATION_NEVER

Review these three features: Execute a non-thing, PROPAGATION_NOT_SUPPORTED –> if there is no current thing, Suspend the current thing, PROPAGATION_NEVER –> executes in a non-thing fashion, and throw an exception if something currently exists

Because there is no present thing, according to these properties, there is no need to open the real thing.

4. Create a TransactionInfo object

Assuming that the transaction has been created (the creation of the transaction was not analyzed in the previous step and will be left to the next article), it is time to create the TransactionInfo object that holds the transaction information created in the previous step.

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @nullable TransactionStatus status) {// Create TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification); // If the transaction label is not empty, assign the TransactionStatus object to TransactionInfoif(txAttr ! = null) { // We need a transactionfor this method...
        if (logger.isTraceEnabled()) {
            logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        // The transaction manager will flag an error if an incompatible tx already exists.
        txInfo.newTransactionStatus(status);
    } else {
        // The TransactionInfo.hasTransaction() method will return false. We created it only
        // to preserve the integrity of the ThreadLocal stack maintained in this class.
        if (logger.isTraceEnabled()) {
            logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");
        }
    }

    // We always bind the TransactionInfo to the thread, even if we didn't create // a new transaction here. This guarantees that the TransactionInfo stack // will be managed correctly even if No transaction was created by this aspect. // Here: TransactionInfo is bound to the current thread whether or not a new transaction is started to ensure the integrity of the TransactionInfo stack. txInfo.bindToThread(); return txInfo; }Copy the code

It’s basically a simple assignment and at the end binds the created TransactionInfo to the current thread. Note that during the binding process, the current transaction information takes the existing transaction information and binds it together to the transactionInfoHolder variable. So you might have a chain of things.

private void bindToThread() {
    // Expose current TransactionStatus, preserving any existing TransactionStatus
    // for restoration after this transaction is complete.
    this.oldTransactionInfo = transactionInfoHolder.get();
    transactionInfoHolder.set(this);
}
Copy the code

Where transactionInfoHolder is a variable of type ThreadLocal:

private static final ThreadLocal<TransactionInfo> transactionInfoHolder = new NamedThreadLocal<>("Current aspect-driven transaction");
Copy the code

This analysis is over, because there are too many branches here, or open a few more!