One, foreword

Transaction management is very important for system application, it ensures data integrity and security. Especially for financial services, it is indispensable. The classic scenario is the transfer operation, in which account A transfers 5000 yuan to account B, the balance of ACCOUNT A decreases 5000 yuan, and then the balance of account B increases 5000 yuan. In most cases, the transaction will be completed normally. But it is also inevitable to encounter failures. At this time, the balance of A cannot be reduced, but the balance of B does not increase.

Before looking at the source code, let’s take a look at some transaction properties in Spring.

Second, transaction attributes

1. Transaction isolation level

Transaction isolation level, which defines the extent to which a transaction may be affected by the activities of other concurrent transactions.

In an application, multiple transactions run simultaneously, often manipulating the same data to accomplish the same work. Concurrency is inevitable, but can lead to the following problems.

  • Dirty reads. There are T1 and T2 transactions. T1 overwrites the data but has not committed it yet, while T2 can read the overwritten data.

  • Nonrepeatable read. There are two transactions, T1 and T2. T1 executes the same query multiple times, but gets different results. This is usually because T2 updates the data during T1 queries.

  • Phantom reads. There are two transactions, T1 and T2. When T1 is reading a record, T2 concurrently inserts a record, and a phantom read occurs. It’s similar to unrepeatable reading.

Ideally, all transactions should be isolated to prevent this from happening. Completely isolating transactions, however, significantly reduces performance. Because isolation locks rows or tables, it impedes concurrency and requires transactions to wait for each other to complete their work. As a result, several isolation levels are differentiated to flexibly respond to data requirements in different scenarios.

Isolation level meaning
DEFAULT This is the transaction isolation level default in Spring. It represents the default isolation level for using the underlying database. The default value for MySQL is repeatable read, and the default value for Oracle is committed read.
READ_UNCOMMITTED Uncommitted read allows uncommitted data to be read. This results in dirty reads, unrepeatable reads, and phantom reads.
READ_COMMITTED Committed read, allowing read of committed transaction data. Dirty reads can be prevented, but unrepeatable and phantom reads can still occur.
REPEATABLE READ Repeatable reads, in which multiple reads of the same field yield the same result, unless the data is changed by the current transaction itself. Dirty and unrepeatable reads can be avoided, but phantom reads can still occur.
SERIALIZABLE Serialization, where all transactions are executed one by one so that interference between transactions is completely impossible. In fact, this level is rarely used.

2. Transaction propagation behavior

The propagation behavior of a transaction means that there are several options to specify the execution behavior of a transactional method if a transaction context already exists before the current transaction is started. Seven propagation behaviors are defined in Spring.

Propagation behavior meaning
PROPAGATION_MANDATORY Indicates that the method must run in a transaction. If there is no transaction currently, an exception is thrown.
PROPAGATION_NEVER Indicates that the method should not run in a transaction. Throws an exception if a transaction is running.
PROPAGATION_NOT_SUPPORTED Indicates that the method should not run in a transaction. If an existing transaction is in progress, it will be suspended while the method is running.
PROPAGATION_SUPPORTS Indicates that the current method does not require a transactional context, but can run in a transaction if one is already running.
PROPAGATION_REQUIRES_NEW Indicates that the current method must run in its own transaction. A new transaction will be started and, if there is an existing transaction running, will be suspended while the method is running.
PROPAGATION_REQUIRES Indicates that the current method must run in a transaction. If an existing transaction is in progress, the method will run in that transaction, otherwise a new transaction will be started.
PROPAGATION_NESTED If a transaction exists, a transaction is created to run as a nested transaction of the current transaction; If there are no transactions, this value is equivalent to PROPAGATION_REQUIRED.

3. Transaction timeout

A transaction timeout is the maximum time that a transaction is allowed to execute, and if the transaction has not completed after this time, the transaction is automatically rolled back.

4. Read-only properties

The read-only property of a transaction refers to read-only or read-write operations on transactional resources. If a transaction only reads to a back-end database, that database may take advantage of the read-only nature of that transaction to take some optimization measures. By declaring a transaction read-only, you give the back-end database an opportunity to apply any optimizations it deems appropriate.

5. Rollback rules

Normally, if an unchecked exception (an exception inherited from RuntimeException) is thrown in a transaction, the transaction is rolled back by default. If no exception is thrown, or if a checked exception is thrown, the transaction is still committed. This is usually the way most developers want to handle it, and it’s the default handling in EJBs. However, we can artificially control the transaction to commit the transaction if some unchecked exception is thrown, or roll back the transaction if some checked exception is thrown, as needed.

The three interfaces for Spring transactions

1, the PlatformTransactionManager

PlatformTransactionManager is the abstraction layer of transaction management, Spring according to the abstraction layer provides many different concrete realization. Such as DataSourceTransactionManager, JpaTransactionManager, HibernateTransactionManager, etc.

Public interface PlatformTransactionManager {/ / return currently active transaction or create a new transaction. // Definition describes transaction properties, such as propagation behavior, isolation level, TransactionStatus getTransaction(TransactionDefinition Definition) throws TransactionException // Commit a given transaction based on the status of the given transaction void commit(TransactionStatus status) throws TransactionException; Void rollback(TransactionStatus status) throws TransactionException; // Perform the rollback for the given transaction. }Copy the code

2、 TransactionDefinition

Defines transaction attributes

Public interface TransactionDefinition {// Transaction 7 propagation actions 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; Int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; // Transaction timeout int TIMEOUT_DEFAULT = -1; // Return propagationBehavior int getationBehavior (); Int getIsolationLevel(); Int getTimeout(); Boolean isReadOnly(); // Return the transaction name String getName(); }Copy the code

3、 TransactionStatus

Represents the state of the current transaction and can also control the current transaction.

Public interface TransactionStatus extends SavepointManager, Flushable {Boolean isNewTransaction(); HasSavepoint (); hasSavepoint(); // Set the current transaction to be rolled back. If this is set, commit does not work voidsetRollbackOnly(); // Whether the transaction should be rolled back Boolean isRollbackOnly(); Void flush(); void flush(); void flush(); // Whether the current transaction has completed Boolean isCompleted(); }Copy the code

Fourth, the implementation of transactions

Transaction implementations in Spring generally fall into two categories: programmatic transactions and declarative transactions.

1. Programmatic transactions

As the name implies, programmatic transactions control the execution of transactions programmatically. Let’s look at an example. PlatformTransactionManager USES Spring JDBC transaction manager. The data source is a database connection pool, so let’s take a look at the XML configuration.

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">  
	<property name="driverClassName" value="${jdbc.driverClassName}" />  
	<property name="url" value="${jdbc.url}" />  
	<property name="username" value="${jdbc.username}" />  
	<property name="password" value="${jdbc.password}" />  
</bean>
 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
	<property name="dataSource" ref="dataSource" />  
</bean>  

<bean id="transactionDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition">
	<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
Copy the code

You can control the running of a transaction directly through the transaction manager in your code. Let’s look at a method in a Service.

public void insertUser(User user) {
	TransactionStatus txStatus = transactionManager.getTransaction(transactionDefinition);
	try {
		System.out.println("---------- New user information ------------");
		transactionManager.commit(txStatus);
	} catch (Exception e) {
		System.out.println("Error sending save user information,"+e); transactionManager.rollback(txStatus); }}Copy the code

This completes the management of business methods through programmatic transactions. Of course, its disadvantages are also obvious, transaction code and business code are mixed together, breaking business code organization, and is not conducive to maintenance and extension. Is there a better way to do it? What do you do with Spring AOP, which we learned in the previous section?

Declarative transactions

That’s right, use Spring AOP to intercept methods. Create or join a transaction before the method starts and commit or roll back the transaction after the method completes, depending on the situation. This is a declarative transaction. Let’s look at a namespace-based declarative transaction management.

First, configure a transaction manager.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
	<property name="dataSource" ref="dataSource" />  
</bean>  
Copy the code

Second, configure an Advisor with an ADVICE and a pointcut through the AOP tag.

<aop:config>  
	<aop:pointcut id="serviceMethods" 
		expression="execution(* com.viewscenes.netsupervisor.service.. * (..) )"/>  
	<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>  
</aop:config>  
Copy the code

Finally, define an advice with the TX tag. It is itself a notification of a transaction, currently containing the transaction’s manager and transaction properties.

<tx:advice id="txAdvice" transaction-manager="transactionManager">  
	<tx:attributes>  
		<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
		<tx:method name="*"/>  
	</tx:attributes>  
</tx:advice>  
Copy the code

This way, our business code does not need to add any code about the transaction to complete the operation of the transaction. In real development, we mostly configure transactions this way or through annotations rather than programmatically.

Five, source code analysis

Long winded so much, is to put the transaction of the running rules, attributes clearly, or up is the source code, easy to carsick ha. Source tx tag as an example of the configuration of the way to analyze, programmatic transactions and annotations annotations transaction this chapter is not involved.

1. Tx label resolution

Tx label, in the Spring when scanning XML is loaded into, specific location to org. Springframework. Transaction. Config. TxAdviceBeanDefinitionParser class. Then calls the class of the parse () method, but we found that such is not the parse method, to find the final call up to the parent class’s superclass AbstractBeanDefinitionParser. The parse (). Finally, a constructed BeanDefinition object is returned and registered in the bean container for further instantiation. The whole process can be divided into three steps.

  • Create TransactionInterceptor

The TransactionInterceptor class is the TransactionInterceptor class that implements the MethodInterceptor interface. When the invoke method of a proxy class is called, the invoke of that class is actually called.

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); / / getBeanClass call to subclass method, this subclass is TxAdviceBeanDefinitionParser / / it is returnedreturnTransactionInterceptor.class; Class<? > beanClass = getBeanClass(element);if(beanClass ! = null) { builder.getRawBeanDefinition().setBeanClass(beanClass); } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); / / call subclasses TxAdviceBeanDefinitionParser methoddoParse(element, parserContext, builder);
	return builder.getBeanDefinition();
}
Copy the code
  • 2. Resolve transaction attributes

The first step is to create the BeanDefinition object of the TransactionInterceptor, and then call the subclass’s doParse method to resolve the child node for adding transaction attributes.

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {/ / will configure the transaction manager is added to the property builder. AddPropertyReference ("transactionManager", TxNamespaceHandler.getTransactionManagerName(element)); // Get the sub-tag of tx <tx: Attributes >. Can only have a child tag List here specified Element < > txAttributes = DomUtils. GetChildElementsByTagName (Element, ATTRIBUTES_ELEMENT);if (txAttributes.size() > 1) {
		parserContext.getReaderContext().error(
				"Element <attributes> is allowed at most once inside element <advice>", element);
	}
	else if(txAttributes.size() == 1) { Element attributeSourceElement = txAttributes.get(0); // Resolve the properties of the transaction method. The tag <tx:method name="insert*"/> RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext); / / add NameMatchTransactionAttributeSource instance of a class to the property. / / NameMatchTransactionAttributeSource is a contains all instances of the transaction attribute builder. AddPropertyValue ("transactionAttributeSource", attributeSourceDefinition);
	}
	else {
		builder.addPropertyValue("transactionAttributeSource",
				new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource")); } } private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext ParserContext) {// Get all the child tags tx:method // for example <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT); ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap = new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size()); transactionAttributeMap.setSource(parserContext.extractSource(attrEle)); // Add the name of method tag as key and the other transaction attributes as value to the Mapfor (Element methodEle : methods) {
		String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
		TypedStringValue nameHolder = new TypedStringValue(name);
		nameHolder.setSource(parserContext.extractSource(methodEle));

		RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
		String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
		String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
		String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
		String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
		if (StringUtils.hasText(propagation)) {
			attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
		}
		if (StringUtils.hasText(isolation)) {
			attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
		}
		if (StringUtils.hasText(timeout)) {
			try {
				attribute.setTimeout(Integer.parseInt(timeout));
			}
			catch (NumberFormatException ex) {
				parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle); }}if (StringUtils.hasText(readOnly)) {
			attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
		}

		List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
		if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
			String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
			addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
		}
		if(methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) { String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE); addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue); } attribute.setRollbackRules(rollbackRules); transactionAttributeMap.put(nameHolder, attribute); } / / create NameMatchTransactionAttributeSource class, Put the above transaction attribute map in their properties nameMap RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class); attributeSourceDefinition.setSource(parserContext.extractSource(attrEle)); attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
	return attributeSourceDefinition;
}
Copy the code

The TransactionInterceptor object is then returned with the transaction manager and all transaction properties in its properties collection.

  • 3. Register TransactionInterceptor

Register this class with the bean container. Its name is txAdvice configured in XML.

this.beanDefinitionNames.add(beanName);
this.beanDefinitionMap.put(beanName, beanDefinition);
Copy the code

2. Advisor parsing

Advisor parsing has been thoroughly analyzed in the previous chapter, but will not be expanded here. In short, it finally returned to a BeanDefinition DefaultBeanFactoryPointcutAdvisor instance object, the key is that it has an attribute adviceBeanName is txAdvice above has been registered with the container.

All that remains is the process of AOP generating the proxy and actually calling the Service method to the AopProxy invoke method. Or in the JDK dynamic proxy, for example, when to invoke the Service method, call to JdkDynamicAopProxy. The invoke (). As you may remember, it first fetches the interceptor chain of methods, that is, the collection of notification methods, and then chases their invoke. Here, only a notification, that is org. Springframework. Transaction. The interceptor. TransactionInterceptor.

3. Transaction control

TransactionInterceptor. Invoke () is the place where the actual transaction. Let’s take a look at the big picture.

public class TransactionInterceptor{ public Object invoke(final MethodInvocation invocation) throws Throwable { / / the target Class Class object/Class/com.viewscenes.net supervisor. Service. Impl. UserServiceImpl Class <? > targetClass = AopUtils.getTargetClass(invocation.getThis()); // Transaction processingreturn invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallbackPublic Object proceedWithInvocation() throws Throwable {returninvocation.proceed(); }}); }}Copy the code

As you can see, the invokeWithinTransaction method is the focus. Again, let’s take a look at its internal implementation.

protected Object invokeWithinTransaction(Method method, Class<? > targetClass, Final InvocationCallback Invocation throws Throwable {// Get transaction attributes (propagation behavior, isolation level, etc.) Final TransactionAttribute txAttr = getTransactionAttributeSource(). getTransactionAttribute(method, targetClass); / / get the transaction manager final PlatformTransactionManager tm = determineTransactionManager (txAttr); / / / com.viewscenes.net/execution method. The supervisor service. Impl. UserServiceImpl. InsertUser final String joinpointIdentification = methodIdentification(method, targetClass); / / thisifDeclarative transactions,elseIt is a programmatic transaction, not involved at the moment.if(txAttr == null || ! (tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification) Object retVal = null; The try {// callback method calls the next interceptor chain. // But there is really only one notification, so the target object's actual method is called. retVal = invocation.proceedWithInvocation(); } the catch (Throwable ex) {/ / rollback rollback transaction completeTransactionAfterThrowing (txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } / / commit to commit the transaction commitTransactionAfterReturning (txInfo);returnretVal; }}Copy the code

See the above source, the train of thought is more clear.

  • Gets the transaction manager and transaction properties
  • Implementing business methods
  • Decide whether to roll back or commit the transaction based on try and catch

So this also explains why there is no catch exception in business methods, otherwise the transaction will not be rolled back. If you must catch the exception and hold the transaction, it is possible to throw the exception manually after the catch. As follows:

public void insertUser(User user) {	
	try {
		int i = 1/0;
		System.out.println("---------- New user information ------------");
	} catch (Exception e) {
		System.out.println("UserServiceImpl.insertUser()"+e.getMessage()); throw new RuntimeException(); }}Copy the code

In thorw, however, it is important to note that Spring has a judgment when it comes to rolling back. That is, only these two exceptions will be rolled back.

public boolean rollbackOn(Throwable ex) {
	return (ex instanceof RuntimeException || ex instanceof Error);
}
Copy the code

So, what else is there besides this manual throw? Of course, remember there’s a method in the TransactionStatus interface called setRollbackOnly. We can set it so that transactions can only be rolled back and not committed. It doesn’t matter if you go to the commit method, which has a judgment.

if (defStatus.isLocalRollbackOnly()) {
	if (defStatus.isDebug()) {
		logger.debug("Transactional code has requested rollback");
	}
	processRollback(defStatus);
	return;
}
Copy the code

Call it in business code, like this. You can also guarantee that the transaction will be rolled back.

public void insertUser(User user) {		
	try {
		int i = 1/0;
		System.out.println("---------- New user information ------------"); } catch (Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }}Copy the code

Finished the whole process, we look back to createTransactionIfNecessary method. Because we want to understand how transactions are actually created.

  • Get a transaction manager from the data source
package org.springframework.jdbc.datasource; Public class DataSourceTransactionManager {/ / create a data source transaction manager, from a data source access to an underlying database connection / / conHolder right now or is empty protected ObjectdoGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		ConnectionHolder conHolder =
			(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
		txObject.setConnectionHolder(conHolder, false);
		returntxObject; }}Copy the code
  • Judge transaction attributes

Determine transaction attributes, whether timeout, propagation behavior

// Transaction timed outif (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
		throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // indicates that this method must run in a transaction. However, the transaction has not been started yet, so an exception is thrown.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
	throw new IllegalTransactionStateException(
			"No existing transaction found for transaction marked with propagation 'mandatory'");
}
Copy the code
  • Enable transactions Transactions are bound to the underlying database connection.
protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; // Get a connection from the data source, And put in the transaction manager / / newCon is ProxyConnection [a javax.sql.pooledconnection [com. Mysql. JDBC. JDBC4Connection @ 62 e9f76b]] / / this means that a transaction corresponds to a database connection Connection newCon = this.dataSource.getConnection(); txObject.setConnectionHolder(new ConnectionHolder(newCon),true);
		txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); // Set transaction commit mode to manual commitif (con.getAutoCommit()) {
			txObject.setMustRestoreAutoCommit(true);
			con.setAutoCommit(false); } / / set the transaction connection state txObject. GetConnectionHolder () setTransactionActive (true); Int timeout = determineTimeout(definition);if(timeout ! = TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // bind the session to the thread threadLocalif(txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); }}}Copy the code
  • Encapsulating transaction objects

Encapsulate the transaction manager, transaction properties, and execution methods into TransactionInfo objects, set the state of the transaction, bind to the current thread, and return.

protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinpointIdentification, TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);if(txAttr ! = null) {/ / set the transaction state txInfo. NewTransactionStatus (status); } // bind the transaction to the current thread, ThreadLocal. // Txinfo.bindtoThread () will not commit;return txInfo;
}
Copy the code

Six, summarized

In general, Spring’s transaction management is divided into programmatic and declarative transactions.

  • Based on TransactionDefinition, PlatformTransactionManager, TransactionStatus programmatic transaction management is to provide the most primitive way of Spring, we usually won’t write, But understanding this approach goes a long way toward understanding the nature of Spring transaction management.

  • Declarative transaction management based on and namespace is currently recommended. Its biggest characteristic is closely combined with Spring AOP, which can make full use of the powerful support of pointcut expression, making transaction management more flexible.

Declarative transactions based on tx tags are closely integrated with AOP, and by intercepting methods, they are actually processed by invoking the same interface methods used in programmatic transactions.