preface
We all know what transactions are, and Spring transactions use AOP to provide declarative and programmatic transactions on the database to help us simplify development and decouple business logic and system logic. But what is the principle of Spring transactions? How do transactions propagate between methods? Why do transactions fail sometimes? The next few articles will focus on the analysis of Spring transaction source code, so that we can thoroughly understand the principle of Spring transaction.
Parsing of XML tags
<tx:annotation-driven transaction-manager="transactionManager"/>
It should be familiar to those who have configured transactions. The above configuration is the configuration supported by the Spring open transaction annotation (@ Transactional). Those who have read my previous articles should know that this prefixed tag is called a custom tag. I also analyzed the parsing process of custom tags in my previous articles, so I directly find the corresponding handler here:
public class TxNamespaceHandler extends NamespaceHandlerSupport { static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager"; static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; static String getTransactionManagerName(Element element) { return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ? element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME); } @Override public void init() { registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser()); registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser()); } }
You can see that the corresponding annotation parser is
AnnotationDrivenBeanDefinitionParser class, in which there must be a parse method:
public BeanDefinition parse(Element element, ParserContext parserContext) { registerTransactionalEventListenerFactory(parserContext); String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // mode="aspectj" registerTransactionAspect(element, parserContext); if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) { registerJtaTransactionAspect(element, parserContext); } } else { // mode="proxy" AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null; }
First, get the value of the mode attribute to determine whether to use AspectJ to generate proxy or JDK to generate proxy. Here, we mainly look at the proxy mode and enter the configureAutoProxyCreator method:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { // Register the entry class of AOP AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource = parserContext.extractSource(element); // Create the TransactionAttributeSource definition. // @Attribute encapsulation of Transactional annotations RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. // AOP execution chain RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // Get the value of the transaction manager attribute registerTransactionManager(element, interceptorDef); interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); // Create the TransactionAttributeSourceAdvisor definition. RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName)); parserContext.registerComponent(compositeDef); } }
The process here is relatively long, but the logic is very simple. First, let's look at the AOP entry class for registration transactions:
public static void registerAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { // Put the AOP entry class with higher priority into the IOC container BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // Set how the proxy is generated and whether to cache the proxy class to the current thread useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); registerComponentIfNecessary(beanDefinition, parserContext); }
Mainly look
Registerautoproxycreator ifnecessary method:
public static BeanDefinition registerAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); } private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // Judge whether the class passed in and the class currently existing in ICO have higher priority, and put the higher class into the IOC if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } //Encapsulate the AOP entry class as a beanDefinition object and instantiate it RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //Annotate the beanName name of aop entry class aopconfigutils AUTO_ PROXY_ CREATOR_ BEAN_ NAME registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
First, judge whether the AOP entry class already exists in the container. If it does not exist, create it directly
The BeanDefinition object of infrastructure advisor autoproxycreator is registered in the container. This class is also a subclass of AOP entry class AbstractAutoProxyCreator. Let's take a look at its inheritance relationship:
Do you wonder which of these subclasses will be used? Returning to the code just now, we can see that if an entry class already exists, the priority of the two classes will be obtained through findPriorityForClass, and finally the one with higher priority will be used. What is their priority order?
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3); static { // Set up the escalation list... APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); } private static int findPriorityForClass(@Nullable String className) { // The index is the priority. The higher the priority, the higher the priority. There will only be one transaction AOP entry class in the IOC for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) { Class<?> clazz = APC_PRIORITY_LIST.get(i); if (clazz.getName().equals(className)) { return i; } } throw new IllegalArgumentException( "Class name [" + className + "] is not a known auto-proxy creator class"); }
As you can see,
Infrastructure advisor autoproxycreator has the lowest priority and will not work basically; AspectJAwareAdvisorAutoProxyCreator is the entry class of AOP configured in xml, which will be registered when the < AOP: config > tag is configured; The AnnotationAwareAspectJAutoProxyCreator is registered when < AOP: AspectJ AutoProxy > is configured or @ EnableAspectJAutoProxy annotation is used. Therefore, the AnnotationAwareAspectJAutoProxyCreator is used in most cases.
After registering the AOP entry class, return to the configureAutoProxyCreator method:
RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName =parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
The function of AnnotationTransactionAttributeSource class is to encapsulate the attributes of transaction annotation @ Transactional. Here, you need to remember its inheritance system and be familiar with the attributes and methods of this class and its parent class, which is helpful to analyze the execution principle of transaction aspects later:
Then we created the TransactionInterceptor object, a special transaction Interceptor, and this class is a subclass of MethodInterceptor. It should be no stranger to see this. We know that the AOP call chain mainly calls the invoke method of this class during execution, so it is the entry for transaction aspect execution. Now that there is an Interceptor, it is also necessary to have an Advisor, which is composed of Advice and Poincut, so as to form a complete aspect. Therefore, these two objects are created after this method. The above is the principle of xml configuration AOP annotation support. It is very simple. Let's see how zero configuration is implemented.
AOP zero configuration principle
Anyone who has used SpringBoot knows that if you need to turn on the support of transaction annotations, you only need one annotation:@
EnableTransactionManagement does not need to configure xml files. How does this work? Not much to say, let's look directly at its source code:
@Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
Under this annotation, a class is imported using @ Import
TransactionManagementConfigurationSelector: firstly, the annotation is used to import an instance of a class into the IOC container. You may say that it is OK to add @ Component annotation on the class, but some classes are not in the path you scan, and the annotation can still be imported, So we mainly look at what is done in the TransactionManagementConfigurationSelector class:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: 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); } }
You can see that autoproxyregister and are returned in the selectImports method
ProxyTransactionManagementConfiguration class will be encapsulated as BeanDefinition object after returning. Where is this method called? This was also analyzed in the previous article. The ConfigurationClassPostProcessor class will call the parse method of the ConfigurationClassParser class to parse @ Configuration, @ Import, @ ImportSource and other annotations. The specific process will not be repeated here. Let's continue to look at the autoproxyregister and ProxyTransactionManagementConfiguration classes respectively:
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> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); if (candidate == null) { continue; } Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) { //Register the entry class of transaction AOP, infrastructureasuggestorautoproxycreator. In fact, this AOP entry class does not work AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } } } public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { /* * Obviously, create transaction facet instances * BeanFactoryTransactionAttributeSourceAdvisor * * */ @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource()); //Set notification class advisor.setAdvice(transactionInterceptor()); if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } /* * Create transaction advice * TransactionInterceptor * */ @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); //The transaction manager needs to be linked to the data source, so it needs to be defined by itself if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
It is clear that the former is the entry class for registering AOP (the entry class registered here is still
Infrastructure advisor autoproxycreator), which is an instance of the component that creates transaction AOP into the IOC. I believe that I know not only the zero configuration of transactions, but also the zero configuration implementation principle of the whole SpringBoot.
Author: don't talk at night
Original link:
https://blog.csdn.net/l6108003/article/details/106650023