Spring transaction @EnableTransactionManagement

Foreword

@EnableTransactionManagement is an annotation provided by the Spring framework to enable annotation-based transaction management. By using this annotation, you can enable Spring’s automatic management of transactions, allowing you to use the @Transactional annotation on methods to declare transactions.

@EnableTransactionManagement mainly has the following two attributes:

  1. mode (default is AdviceMode.PROXY): Specifies the mode of the transaction agent. Possible values to choose from are AdviceMode.PROXY and AdviceMode.ASPECTJ. The default is AdviceMode.PROXY, which means using proxy-based transaction management. If you select AdviceMode.ASPECTJ, it means using AspectJ-based method to implement transaction management.

  2. proxyTargetClass (default false): Used to determine whether the proxy should use the target class’s class proxy, rather than the interface implemented by the target class. If set to true, the proxy will use the class proxy of the target class (CGLIB proxy). If set to false, the proxy will use the interface proxy implemented by the target class (JDK dynamic proxy). ).

When you use the @EnableTransactionManagement annotation on a configuration class, the Spring framework will automatically scan for methods annotated with @Transactional and weave transaction management logic around these methods. . If an exception occurs during the execution of a method annotated with @Transactional, Spring will automatically roll back the transaction. The purpose of this annotation is to simplify the configuration and use of transaction management and improve development efficiency.

Example:

@Configuration
@EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false)
public class AppConfig {<!-- -->
    // ...
}

In this example, @EnableTransactionManagement enables proxy-based transaction management, using the default proxy mode and interface proxy. You can adjust the values of these two properties as needed.

Source code analysis

First check the @EnableTransactionManagement annotation and use the @import annotation to import a class: TransactionManagementConfigurationSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {<!-- -->
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}

TransactionManagementConfigurationSelector

By default mode = AdviceMode.PROXY, so the selectImports(AnnotationMetadata importingClassMetadata) method will be called when the configuration class is parsed.

That is, the selectImports(AnnotationMetadata importingClassMetadata) method of the AdviceModeImportSelector class will be called, and then the selectImports(AdviceMode adviceMode) method of TransactionManagementConfigurationSelector will be called, which involves the use of ImportSelector.

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {<!-- -->

/**
* Returns {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
* and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
* respectively.
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {<!-- -->
// @EnableTransactionaManagement annotation defaults to Proxy
switch (adviceMode) {<!-- -->
case PROXY:
//Then we need to load the classes in this array. At this point, the @EnableTransactionManagement annotation is completed.
return new String[] {<!-- -->AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {<!-- -->determineTransactionAspectClass()};
default:
return null;
}
}

private String determineTransactionAspectClass() {<!-- -->
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME:
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}

}

The String array returned by selectImports(AdviceMode adviceMode) will convert the classes in this array into BeanDefinitions and register them in the container.

The ConfigurationClassParser class is the configuration class responsible for parsing the @Configuration annotation.

The ConfigurationClassBeanDefinitionReader class is an auxiliary class of ConfigurationClassParser, which is used to convert the parsed configuration class information into a BeanDefinition object and register it in the Spring container.

The main responsibility of the ConfigurationClassParser class is to scan and parse configuration classes with @Configuration annotations. It will parse various annotations, methods and fields in the class to determine the beans to be created and configured. It processes @Configuration annotations, @Bean annotations, @ComponentScan annotations, @Import annotations, etc., and generates corresponding BeanDefinition objects based on these annotations.

Once ConfigurationClassParser completes parsing the configuration class, it will pass the parsed BeanDefinition objects to ConfigurationClassBeanDefinitionReader, which is responsible for registering these BeanDefinition objects into the Spring container.

Therefore, ConfigurationClassParser and ConfigurationClassBeanDefinitionReader work together. One is responsible for parsing and processing the @Configuration annotated configuration class, and the other is responsible for converting the parsing results into BeanDefinition and registering it in the container. .

AutoProxyRegistrar

The AutoProxyRegistrar class implements the ImportBeanDefinitionRegistrar interface, and its function is to register the automatic proxy creator (AutoProxyCreator) in the Spring container.

Specifically, the registerBeanDefinitions() method is a method in the ImportBeanDefinitionRegistrar interface, which will be called during the initialization process of the configuration class (or usage). This method receives two parameters:

  • importingClassMetadata: used to obtain metadata of imported classes, such as annotation information, class name, etc.
  • registry: BeanDefinitionRegistry object used to register BeanDefinition.
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {<!-- -->

private final Log logger = LogFactory.getLog(getClass());

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {<!-- -->
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {<!-- -->
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {<!-- -->
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null & amp; & amp; proxyTargetClass != null & amp; & amp; AdviceMode.class == mode.getClass() & amp; & amp;
Boolean.class == proxyTargetClass.getClass()) {<!-- -->
candidateFound = true;
if (mode == AdviceMode.PROXY) {<!-- -->
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {<!-- -->
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound & amp; & amp; logger.isInfoEnabled()) {<!-- -->
String name = getClass().getSimpleName();
logger.info(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}

}

In the registerBeanDefinitions() method, first obtain all annotation types on the configuration class through importingClassMetadata. Then iterate through these annotation types and try to obtain the mode and proxyTargetClass attribute values on the annotation.

If an annotation with mode and proxyTargetClass attributes is found and their types are AdviceMode and boolean respectively, then Indicates that a candidate automatic proxy creator was found. According to the value of the mode attribute, if it is AdviceMode.PROXY, the AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry) method is called to register the automatic proxy creator.

If the proxyTargetClass attribute is true, call the AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry) method to force the automatic proxy creator to use the class proxy mode. Then, the method returns to complete the registration process.

If annotations with mode and proxyTargetClass attributes are not found on the configuration class, or other exceptions occur, a warning log will be printed, indicating that automatic proxying may not be performed as expected. Producer registration and configuration.

In short, the registerBeanDefinitions() method of the AutoProxyRegistrar class determines whether to register the automatic proxy creator by checking the annotations on the configuration class, and configures it accordingly based on the attribute values of the annotations. This can realize the automatic proxy function, which is used to proxy components marked with specific annotations.

InfrastructureAdvisorAutoProxyCreator

First, AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); actually registers the definition information of the beans produced by InfrastructureAdvisorAutoProxyCreator into the container. But in fact there will be an overridden behavior, as shown in the following method code:

private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {<!-- -->
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
\t\t
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {<!-- -->
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {<!-- -->
// currentPriority What is the position of the currently injected class?
//APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); 0
//APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); 1
//APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); 2
// That is to say, the bottom one can cover the top one.
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {<!-- -->
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}

Briefly analyze the implementation of registerOrEscalateApcAsRequired. This method is used to register the automatic proxy creator (AutoProxyCreator) in the BeanDefinitionRegistry and perform necessary upgrades based on priority.

The main logic of the method is as follows:

  1. First, make sure that the BeanDefinitionRegistry parameter passed in is not null, otherwise an exception will be thrown.

  2. Checks whether a BeanDefinition named AUTO_PROXY_CREATOR_BEAN_NAME is already included in the BeanDefinitionRegistry, where AUTO_PROXY_CREATOR_BEAN_NAME is the name of the automatic proxy creator.

  3. If the BeanDefinition of the automatic proxy creator already exists, it is further determined whether the class of the automatic proxy creator currently to be registered is the same as the class of the existing BeanDefinition. If they are different, it means that a higher priority automatic proxy creator has been registered and needs to be upgraded.

  4. Determine whether the class of the automatic agent creator currently being registered has a higher priority by comparing the priorities of the automatic agent creator classes. The priority is determined through the findPriorityForClass method. If the class priority of the automatic proxy creator currently to be registered is higher, update the existing BeanDefinition class to the class currently to be registered.

  5. If the BeanDefinition of the automatic proxy creator does not exist, create a RootBeanDefinition object whose class is the class of the automatic proxy creator currently to be registered.

  6. Set the source object of RootBeanDefinition to the passed in source parameter. This parameter indicates the source of registration.

  7. Set the property value for RootBeanDefinition and set its order to Ordered.HIGHEST_PRECEDENCE to ensure it is created before other beans.

  8. Set the role of RootBeanDefinition to BeanDefinition.ROLE_INFRASTRUCTURE, indicating that it is a Bean with an infrastructure role.

  9. Register RootBeanDefinition into BeanDefinitionRegistry, using AUTO_PROXY_CREATOR_BEAN_NAME as the name of the Bean.

  10. Finally, the registered RootBeanDefinition object is returned.

In summary, this code describes the logic of registering the automatic proxy creator in the BeanDefinitionRegistry and making necessary upgrades based on priority. It ensures that only the automatic proxy creator with the highest priority is registered and set as a bean for the infrastructure role.

However, in actual use, it may be replaced by AnnotationAwareAspectJAutoProxyCreator when AOP and transactions are started at the same time. For details, you can read another article by the blogger: SpringAOP source code analysis and infrastructure registration

ProxyTransactionManagementConfiguration

This class ProxyTransactionManagementConfiguration is a Spring configuration class used to configure transaction management. It inherits from AbstractTransactionManagementConfiguration and is marked with the @Configuration annotation, indicating that this is a configuration class.

The following methods are defined in this class:

  1. transactionAdvisor(): This method is used to create a BeanFactoryTransactionAttributeSourceAdvisor object as the transaction advisor (notifier). Advisor defines at which cut points the transaction logic is applied. This method receives two parameters: transactionAttributeSource (transaction attribute source) and transactionInterceptor (transaction interceptor). It sets these two objects into the created BeanFactoryTransactionAttributeSourceAdvisor and sets the order of the advisors as needed.

  2. transactionAttributeSource(): This method is used to create a AnnotationTransactionAttributeSource object as a transaction attribute source that manages transaction attributes. AnnotationTransactionAttributeSource is an annotation-based transaction attribute source, used to obtain transaction attribute information from methods marked with transaction annotations.

  3. transactionInterceptor(): This method is used to create a TransactionInterceptor object as a transaction interceptor. TransactionInterceptor is an AOP interceptor used to apply transaction logic before and after method calls. It receives a transactionAttributeSource parameter, which is used to obtain transaction attribute information. If a txManager (transaction manager) is configured, set it to the TransactionInterceptor created.

These methods declare themselves as beans using the @Bean annotation and specify their roles as infrastructure roles using the @Role(BeanDefinition.ROLE_INFRASTRUCTURE) annotation. This means that they are components used internally by the framework to support transaction management functionality.

In the configuration, the transactionAdvisor() method creates a transaction advisor, which combines transaction attribute sources and transaction interceptors to enhance pointcuts to achieve transaction management. The transactionAttributeSource() method creates an annotation transaction attribute source for managing transaction attribute information. The transactionInterceptor() method creates a transaction interceptor that applies transaction logic before and after method execution.

By using these methods to create corresponding components and registering them as beans in the Spring container, transaction management and interception can be achieved.

The methods in this class are used to configure related components of transaction management, including transaction advisor, transaction attribute source and transaction interceptor. They are key components that support Spring transaction management and are used to implement declarative transaction functions.

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {<!-- -->

@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// To enhance the cut point, an Advisor is naturally needed
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {<!-- -->
// Instantiate the advisor object
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
//Introduce the second bean
advisor.setTransactionAttributeSource(transactionAttributeSource);
//Introduce the third bean
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {<!-- -->
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}

// This is for managing transaction attributes
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {<!-- -->
return new AnnotationTransactionAttributeSource();
}

//Interceptor required by Advisor
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {<!-- -->
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {<!-- -->
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}

}

InfrastructureAdvisorAutoProxyCreator and AnnotationAwareAspectJAutoProxyCreator

InfrastructureAdvisorAutoProxyCreator and AnnotationAwareAspectJAutoProxyCreator are two key classes in the Spring Framework, used to implement AOP (aspect-oriented programming) functions.

  1. InfrastructureAdvisorAutoProxyCreator is an infrastructure class of the Spring framework, used to automatically create proxy objects to implement AOP. It is a post-processor that detects and automatically creates appropriate proxy objects during Spring container initialization. It is mainly used to support AOP functions based on XML configuration.

  2. AnnotationAwareAspectJAutoProxyCreator is an automatic proxy creator in the Spring framework to support annotation-based AOP functionality. It inherits from InfrastructureAdvisorAutoProxyCreator, and adds parsing and processing of annotations on its basis. It scans the beans in the Spring container, detects the use of AOP-related annotations (such as @Aspect, @Before, @After, etc.), and then automatically creates the corresponding proxy object.

By using these two classes, the Spring framework can automatically create proxy objects based on configuration and annotation information to implement AOP functions. AOP allows developers to apply cross-cutting concerns (Cross-cutting Concerns) to different parts of the application, such as logging, performance monitoring, transaction management, etc., by defining aspects and advices.

In the Spring framework, an InfrastructureAdvisorAutoProxyCreator (also known as the parent class of AnnotationAwareAspectJAutoProxyCreator) is registered by default. This class is an infrastructure class of Spring for supporting AOP functionality.

When you use the @EnableAspectJAutoProxy annotation, Spring will automatically replace InfrastructureAdvisorAutoProxyCreator with the more advanced AnnotationAwareAspectJAutoProxyCreator. AnnotationAwareAspectJAutoProxyCreator inherits from InfrastructureAdvisorAutoProxyCreator, and adds parsing and processing of annotations on its basis to support annotation-based AOP functions.

By using the @EnableAspectJAutoProxy annotation, you can enable annotation-based AOP functionality and configure aspects and advice using annotations without explicitly configuring AOP in XML. That is to say, when you use the @EnableAspectJAutoProxy annotation, Spring will automatically upgrade to AnnotationAwareAspectJAutoProxyCreator, thereby enabling annotation-based AOP functionality.

Summary

This article describes what the @EnableTransactionManagement annotation does during initialization. In fact, it is still closely related to AOP. We will conduct source code analysis on the interception of things and the failure rules of things in the future.