1. Prepare knowledge
1.1 JDBC Transactions
To process transactions, disable the AutoCommit function of the Connection, perform service operations, and then manually commit or rollback.
Connection con = ...
// Turn off auto commit
con.setAutoCommit(false);
// Business processingaDao.insert(...) ; bDao.insert(...) ;try{
/ / submit
con.commit();
}catch (Exception e){
/ / rollback
con.rollback();
}
// Resume automatic commit
con.setAutoCommit(true);
Copy the code
1.2 the Spring AOP
When adding methods to Spring’s AOP proxy classes JdkDynamicAopProxy and ObjenesisCglibAopProxy, there are several steps:
- 1 call
DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice
To get aorg.aopalliance.intercept.MethodInterceptor
The list; - Create with the list of the MethodInterceptor
ReflectiveMethodInvocation
Object and call the proceed() method; - 3 TransactionInterceptor is one of the methodinterceptors on the list, used to execute transactions.
For a concrete implementation of Spring AOP, see the Spring AOP source code implementation step by step parsing.
2 Spring transaction control
- Spring’s transaction control uses AOP to retrieve the Connection via DataSource before the business method executes, and setAutoCommit(false); Commit or rollback after the service method is executed.
- The transaction in TransactionInterceptor. Invoke in;
- The parent class of the TransactionInterceptor call
The TransactionAspectSupport invokeWithinTransaction
Method for specific transaction control.
2.1 Several important classes
2.1.1 TransactionAttribute
Transaction property, a subclass of TransactionDefinition
- TransactionDefinition default implementation class
DefaultTransactionDefinition
; - TransactionAttribute implementation classes are inherited DefaultTransactionDefinition;
- TransactionDefinition defines transaction-related attributes:
// Transaction propagation mode
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
/ / TRANSACTION isolation level, using Java. SQL. Connection. The TRANSACTION * definition
int ISOLATION_DEFAULT = -1; // By default, the database isolation level is used
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; // Read uncommitted
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; // Read submitted
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; // Repeatable
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; / / the serialization
Copy the code
- DefaultTransactionDefinition key attributes:
private int propagationBehavior = PROPAGATION_REQUIRED; // Transaction propagation mode
private int isolationLevel = ISOLATION_DEFAULT; // Transaction isolation level
private int timeout = TIMEOUT_DEFAULT; // The timeout period
Copy the code
- Implementation class:
- RuleBasedTransactionAttribute
- DefaultTransactionAttribute
2.1.2 PlatformTransactionManager
Transaction manager
- Methods:
- TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; // Create TransactionStatus according to TransactionDefinition
- void commit(TransactionStatus status) throws TransactionException; / / submit
- void rollback(TransactionStatus status) throws TransactionException; / / rollback
- Implementation class:
- DataSourceTransactionManager
- AbstractPlatformTransactionManager
2.1.3 TransactionStatus
Action class for transaction state
SavepointManager
A subclass of- Various states and Savepoint creation/rollback/release etc
- Implementation class:
- AbstractTransactionStatus
- DefaultTransactionStatus
2.1.4 TransactionInfo
- Class of transaction information, including transaction manager, transaction attribute, etc.
- Through createTransactionIfNecessary create;
- Important attributes:
- PlatformTransactionManager
- TransactionAttribute
- JoinpointIdentification: cut method, such as com. Xx. Yy. The MockService. MockInsert
- TransactionStatus
2.2 Transaction execution process
2.2.1 invokeWithinTransaction
InvokeWithinTransaction is the main flow that executes the transaction
protected Object invokeWithinTransaction(Method method, @NullableClass<? > targetClass,final InvocationCallback invocation) throws Throwable {
// 1. Get transaction attributes, if not, it is not currently in a transaction
/* TransactionAttributeSource: Transaction attribute source, used to obtain transaction attributes, Such as org. Springframework. Transaction. The annotation. AnnotationTransactionAttributeSource TransactionAttribute: transaction attribute, Such as org. Springframework. Transaction. The interceptor. RuleBasedTransactionAttribute * /
TransactionAttributeSource tas = getTransactionAttributeSource();
finalTransactionAttribute txAttr = (tas ! =null ? tas.getTransactionAttribute(method, targetClass) : null);
/ / 2. Obtain PlatformTransactionManager, such as DataSourceTransactionManager.
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
/ / 3. Perform cut method, such as com. Xx, yy. MockService. MockInsert
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null| |! (tminstanceof CallbackPreferringPlatformTransactionManager)) {
// 4. If the transaction does not exist or is not a programed transaction, try to create or obtain the transaction and perform commit/rollback.
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.
// 5 Execute the business code that is the method in the service
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// Rollback to SavePoint if there is SavePoint.
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 6 Run Connection.com MIT ()
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
/ / TransactionTemplate. Execute using TransactionCallback back in the mode of transaction processing
// omit the code}}Copy the code
2.2.2 create TransactionAttribute
When creating TransactionInterceptor Bean ProxyTransactionManagementConfiguration, Through the new AnnotationTransactionAttributeSource created TransactionAttributeSource (), and set into the interceptor.
AnnotationTransactionAttributeSource. GetTransactionAttribute by multi-layer calls, call to determineTransactionAttribute method finally, Use the analytical AnnotatedElement SpringTransactionAnnotationParser;
SpringTransactionAnnotationParser.parseTransactionAnnotation:
- AnnotatedElementUtils. FindMergedAnnotationAttributes AnnotabledElement and Transactional analysis to AnnotationAttributes;
- Parsing AnnotationAttributes, setting propagation types, isolation levels, and so on. Returns the RuleBasedTransactionAttribute.
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
ArrayList<RollbackRuleAttribute> rollBackRules = newArrayList<>(); Class<? >[] rbf = attributes.getClassArray("rollbackFor");
for(Class<? > rbRule : rbf) { RollbackRuleAttribute rule =new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] rbfc = attributes.getStringArray("rollbackForClassName");
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = newRollbackRuleAttribute(rbRule); rollBackRules.add(rule); } Class<? >[] nrbf = attributes.getClassArray("noRollbackFor");
for(Class<? > rbRule : nrbf) { NoRollbackRuleAttribute rule =new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);
return rbta;
}
Copy the code
Then get PlatformTransactionManager
- PlatformTransactionManager already declared on your system when Windows starts up Bean;
- DetermineTransactionManager method, pass
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
To obtain.
2.2.4 create TransactionInfo
TransactionInfo objects in TransactionAspectSupport. CreateTransactionIfNecessary created:
- through
PlatformTransactionManager.getTransaction
Create a TransactionStatus; - With the created TransactionStatus and other properties, pass
prepareTransactionInfo
Method to create a TransactionInfo object.
/ / create TransactionInfo
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if(txAttr ! =null) {
// We need a transaction for 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.
/* Bind TransactionStatus to the current ThreadLocal and back up the old TransactionStatus. * /
txInfo.bindToThread();
return txInfo;
}
Copy the code
2.2.5 create TransactionStatus
By AbstractPlatformTransactionManager. GetTransaction created
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
/ * 1. Obtain DataSourceTransactionObject; DataSourceTransactionManager. DoGetTransaction method if allows nested transactions, allows setting the Savepoint, in support of the nested transactions. * /
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
/ / 2. If the previous did not create TransactionAttribute (TransactionDefinition subclass), is to create a default value DefaultTransactionDefinition here.
definition = new DefaultTransactionDefinition();
}
. / / transaction exists, through DataSourceTransactionObject txObject. GetConnectionHolder () isTransactionActive judgment ()
if (isExistingTransaction(transaction)) {
/ * 3. If there is, according to the definition, getPropagationBehavior () different treatment: (1) PROPAGATION_NEVER: throw new IllegalTransactionStateException (2) PROPAGATION_NOT_SUPPORTED : Suspends the current transaction, the final is performed doSuspend method and implementation for DataSourceTransactionObject setConnectionHolder (null), and bind the DataSource. (3) PROPAGATION_REQUIRES_NEW: Suspend the current transaction, create a new DefaultTransactionStatus, and specify the doBegin method. Create a new transaction. (4) PROPAGATION_NESTED: If nestedTransactionAllowed = = false (the default is false, the open need to manually specify), then throw new NestedTransactionNotSupportedException. Create a new DefaultTransactionStatus and execute createAndHoldSavepoint() to create SavePoint. * /
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// The transaction does not exist
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// Propagation behavior is PROPAGATION_MANDATORY, and an exception needs to be thrown according to the behavior definition.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]." + definition);
}
try {
booleannewSynchronization = (getTransactionSynchronization() ! = SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction,true, newSynchronization, debugEnabled, suspendedResources);
// Execute setAutoCommit, etc
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throwex; }}else {
// 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
2.2.6 doBegin
DataSourceTransactionManager.doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if(! txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {// 1. Obtain the Connection from a DataSource
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 2. Set the ConnectionHolder for later use
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 3. Obtain Connection
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 4. Set readOnly and transactionIsolation for Connection.
txObject.setPreviousIsolationLevel(previousIsolationLevel);
if (con.getAutoCommit()) {
// AutoCommit must be restored after execution
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 4. Disable AutoCommit
con.setAutoCommit(false);
}
/ / prepareTransactionalConnection method, if it is (isEnforceReadOnly () && definition. The isReadOnly ()), 'stmt.executeUpdate("SET TRANSACTION READ ONLY"); `
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if(timeout ! = TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); }// Bind the connection holder to the thread.
if(txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); }}catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null.false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); }}Copy the code