preface
In the last article Spring transaction usage details Described in detail in Spring Let's take a look at the usage process of transactions today Spring How are transactions implemented, such as Spring What does the transaction do during initialization, spring How transactions are committed and rolled back; In order to avoid too long, separate two articles for analysis. This article will analyze it first Spring How the transaction is initialized and what is done during initialization.
Register infrastructure advisor autoproxycreator
We know, want to use Spring Business, you have to turn it on Spring If the transaction function is configured in the configuration file, it needs to be configured in the configuration file < tx:annotation-driven /> Label, then analyze Spring Transaction initialization starts with parsing the tag.
stay TxNamespaceHandler Class init Method, you can see the code logic for parsing the tag:
public class TxNamespaceHandler extends NamespaceHandlerSupport { // ....... @Override public void init() { registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser()); registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser()); } }
So, when parsing < tx:annotation-driven /> When labeling, it will be used AnnotationDrivenBeanDefinitionParser Class parse Method to parse:
// AnnotationDrivenBeanDefinitionParser.java public BeanDefinition parse(Element element, ParserContext parserContext) { this.registerTransactionalEventListenerFactory(parserContext); String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // aspectj pattern this.registerTransactionAspect(element, parserContext); } else { // proxy pattern AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null; }
Next, let's look at the proxy mode, that is, the resolution of the default mode; That is, its inner class is used AopAutoProxyConfigurer of configureAutoProxyCreator Method:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); // ............... }
In this method, the first line is to register Infrastructure advisor autoproxycreator and other bean registrations are ignored here; Let's first look at the registration process:
public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) { // register BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // Handling proxy target class and Expose proxy attribute useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // ...... }
Here and Spring AOP The processing flow is the same. First, register the target bean, reprocessing proxy-target-class and expose-proxy Property, you can refer to: Spring AOP annotation source code parsing( https://my.oschina.net/mengyuankan/blog/2995521)
Register infrastructure advisor autoproxycreator.class
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,Object source) { return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); } private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,Object source) { // Processing Priority 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; } // register 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; }
About this bean The role of is analyzed below AopAutoProxyConfigurer of configureAutoProxyCreator Method in addition to registering the Bean, also registered three beans, these three bean Used to support Spring The entire transaction function of:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { // register InfrastructureAdvisorAutoProxyCreator AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); // Transaction advisor beanname String txAdvisorBeanName = "org.springframework.transaction.config.internalTransactionAdvisor"; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { // register AnnotationTransactionAttributeSource Object eleSource = parserContext.extractSource(element); RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(2); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // register TransactionInterceptor RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(2); AnnotationDrivenBeanDefinitionParser.registerTransactionManager(element, interceptorDef); interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); // register BeanFactoryTransactionAttributeSourceAdvisor RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(2); // And take the two beans registered above as the properties of the bean 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); // Register other event notification components } }
Three registered here bean:
-
BeanFactoryTransactionAttributeSourceAdvisor
-
AnnotationTransactionAttributeSource
-
TransactionInterceptor
It is mainly used to execute the target method, transaction commit and rollback
The three bean relationships are as follows:
InfrastructureAdvisorAutoProxyCreator
What does this class mean? What's the use of it? According to the class name basic Advisor The automatic agent creator does not need to be created for custom advisor. Its class diagram is as follows:
You can see that it's implemented BeanPostProcessor Interface, which was analyzed earlier Spring When you know the relevant source code, the interface is Spring It provides an extension interface with two methods:
public interface BeanPostProcessor { // bean Execute before initialization default Object postProcessBeforeInitialization(Object bean, String beanName){ return bean; } // bean Execute after initialization default Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } }
So when we define a bean After the initialization is completed, it will be executed postProcessAfterInitialization Method, and InfrastructureAdvisorAutoProxyCreator Class implements the method:
public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean != null) { // according to beanClassName and beanName Create a key Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // packing bean return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } // Wrapper bean protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // It has been processed and returned directly if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // Should The bean does not need to be enhanced and returns directly if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // Is it Advice.class, Pointcut.class, Advisor.class, AopInfrastructureBean.class These categories, if yes // These classes, or those that the bean needs to skip, are returned directly if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // If there are enhancements, create a proxy Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // Create proxy Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); // Return agent return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { // .... ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { // CGLIB agent proxyFactory.setProxyTargetClass(true); } else { // JDK interface proxy evaluateProxyInterfaces(beanClass, proxyFactory); } } // Create proxy return proxyFactory.getProxy(getProxyClassLoader()); } // Really create proxy public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } protected final synchronized AopProxy createAopProxy() { //..... return getAopProxyFactory().createAopProxy(this); } public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // judge CGLIB Agency and JDK Conditions of agency if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); // JDK agent if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // CGLIB agent return new ObjenesisCglibAopProxy(config); } else { // JDK agent return new JdkDynamicAopProxy(config); } }
The above code, when we bean After initialization, the bean Create an agent. When creating an agent, create a JDK dynamic agent or CGLIB agent
Spring proxy creation reference: source code analysis of Spring AOP proxy creation( https://my.oschina.net/mengyuankan/blog/2995754)
But is it all bean Will create an agent, no, in the packaging bean Method of wrapIfNecessary In, I will find the bean Corresponding enhancement. If there is a corresponding enhancement, the agent will be created. Therefore, before creating the agent, the agent will be found first Bean corresponding enhancement (interceptor), in wrapIfNecessary The code is in the method:
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
about For Spring transactions, this method returns specificInterceptors It cannot be empty, so we will follow the logic of creating an agent later.
Next, let's look at the implementation process of this method, that is, query the enhancer corresponding to class or method.
Query the enhancer of the corresponding class or method
stay In the getAdvicesAndAdvisorsForBean method, the first step is to find the qualified enhancer, and the second step is to judge whether the enhancer meets the requirements.
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,TargetSource targetSource) { // according to className and beanName Find the qualified intensifier List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } // Find the intensifier protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //Find intensifier List<Advisor> candidateAdvisors = findCandidateAdvisors(); //Judge whether the intensifier meets the conditions List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); //....... return eligibleAdvisors; }
Find enhancer findcandidate Advisors
protected List<Advisor> findCandidateAdvisors() { return this.advisorRetrievalHelper.findAdvisorBeans(); } public List<Advisor> findAdvisorBeans() { String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Get all Advisor.class class // Previously registered bean: BeanFactoryTransactionAttributeSourceAdvisor Also a Advisor, // So, here we should bean Will be extracted and subsequently implanted into the agent advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { // Judge whether the enhancer bean meets the conditions, and return true here if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { //Skip intensifier being created } else { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } } } return advisors; }
Finding all the intensifiers is finding Advisor.class Class,
To illustrate, the bean registered earlier BeanFactoryTransactionAttributeSourceAdvisor, which is also a Advisor, so getting all Advisor.class Class, the bean Extracted; In addition, the Beans also have AnnotationTransactionAttributeSource and TransactionInterceptor These two beans will be woven into the proxy together.
After finding all the enhancements, we need to determine which enhancements meet our goals bean, corresponding method Findadvisors that can apply, the criterion is very simple, that is, go to the method of the target class to find out whether there are transaction annotations @ Transactional. If it exists, it is satisfied.
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { // Used to store qualified enhancements List<Advisor> eligibleAdvisors = new ArrayList<>(); // Processing referral enhancement for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // It has been processed when processing the introduction enhancement continue; } // Ordinary bean if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
Here, if the intensifier meets our goal bean, it will return enhancement, and then the target bean creates a proxy and weaves the enhancement into the proxy.
Judge whether it is satisfied and use canApply method
Resolve @ Transactional
canApply The method is as follows:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { /// pca=BeanFactoryTransactionAttributeSourceAdvisor PointcutAdvisor pca = (PointcutAdvisor) advisor; //pca.getPointcut()=TransactionAttributeSourcePointcut return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { return true; } }
For a transaction bean In general, this enhancement Advisor It was registered earlier BeanFactoryTransactionAttributeSourceAdvisor, which bean Realized PointcutAdvisor Interface, so it will pass through the second if Judgment; After passing getPointcut obtain TransactionAttributeSourcePointcut Object to continue calling overloaded canApply The method is as follows:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { // Get method matcher // The matcher at this time is TransactionAttributeSourcePointcut MethodMatcher methodMatcher = pc.getMethodMatcher(); // If all methods can be matched, the circular method will not be judged if (methodMatcher == MethodMatcher.TRUE) { return true; } // Store the target class and all interfaces of the target class Set<Class<?>> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); // Traverse the methods under the target class and all interfaces of the target class, // If any method can meet the requirements of the enhancer, that is, it has @ Transactional annotation, it will be returned directly for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : // Whether the matching method meets the conditions methodMatcher.matches(method, targetClass)) { return true; } } } return false;
Here, you can only judge whether it conforms to the enhancer according to the method, but not according to the class and interface, because the transaction annotation @ Transactional It can be placed on a class or interface, and all The public method is useful; In fact, the judgment condition for classes and interfaces is the matcher Method, the use of the call TransactionAttributeSourcePointcut of mathcer The method is as follows:
public boolean matches(Method method, Class<?> targetClass) { // ..... TransactionAttributeSource tas = getTransactionAttributeSource(); return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); }
Here, it is determined whether the conditions are met, that is, whether the corresponding method has @ Transactional Notes.
there tas namely Annotation transaction attribute source, that is, the three registered at the beginning of the article bean One of its getTransactionAttribute Get the transaction attribute as follows:
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) { // ignore Object Method of if (method.getDeclaringClass() == Object.class) { return null; } // Create a key based on method and class Object cacheKey = getCacheKey(method, targetClass); // Query transactions according to the key, TransactionAttribute cached = this.attributeCache.get(cacheKey); if (cached != null) { //... // Already judged return cached; } else { TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); //.... return txAttr; } }
computeTransactionAttribute The method is as follows:
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // If it is not a public method, skip if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // Methods of implementing classes Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // First, query the annotation on the method of the implementation class. If it is found, it will be returned directly TransactionAttribute txAttr = findTransactionAttribute(specificMethod); if (txAttr != null) { return txAttr; } // If the annotation cannot be found on the corresponding method of the implementation class, find the annotation on the implementation class of the method and return it directly txAttr = findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } // Existing interface if (specificMethod != method) { // Find the annotation on the interface method and return it directly txAttr = findTransactionAttribute(method); if (txAttr != null) { return txAttr; } // If the annotation cannot be found on the interface method, look for the annotation on the interface txAttr = findTransactionAttribute(method.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } } return null; }
In the above computeTransactionAttribute method, you can see that to find is an annotation on a transaction method. First, find it on the method of the implementation class and return it directly. If it is not found, find the annotation on the implementation class; If the implementation class cannot be found, go to the method on the interface. If the interface method cannot be found, go to the interface again; Therefore, it can be seen here that the transaction annotation placed on the class or interface can act on all public methods under it, and the transaction annotation on the method should take precedence over the annotation on the class or interface, that is, if the transaction annotation is added to the class, interface and method, the annotation on the method will prevail, followed by the class and finally the interface.
After that, the annotation attribute is parsed. The corresponding method is: findTransactionAttribute:
protected TransactionAttribute findTransactionAttribute(Method method) { return determineTransactionAttribute(method); } protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { // Transaction annotation parser for (TransactionAnnotationParser annotationParser : this.annotationParsers) { TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element); if (attr != null) { return attr; } } return null; } // Get on method Transactional annotation public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { // Get on method Transactional annotation AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false); if (attributes != null) { // analysis Transactional Annotation Properties return parseTransactionAnnotation(attributes); } else { return null; } } // analysis Transactional Annotation Properties 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")); List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(); for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("rollbackForClassName")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } rbta.setRollbackRules(rollbackRules); return rbta; }
You can see the transaction annotation here @ Transactional Various properties of.
Of course, it's not over yet; Here, you can look at all the code from bottom to top when the method exists @ This method will satisfy the enhancer when the Transactional annotation is used BeanFactoryTransactionAttributeSourceAdvisor , Then it will be our goal bean Create agents and add enhancers BeanFactoryTransactionAttributeSourceAdvisor Woven into the agent, which in turn affects our business logic.
summary
This article mainly introduces Spring Transaction initialization function, in Spring When loading, it will register InfrastructureAdvisorAutoProxyCreator Bean, and the bean Again BeanPostProcessor Interface, so when the initialization of the target bean is completed, it will execute postProcessAfterInitialization Method, in which we will traverse our target bean Method in, if there is @ Transactional transaction annotation, it will be the target bean Create agents and add enhancers BeanFactoryTransactionAttributeSourceAdvisor Weaving into the agent; Of course, beanfactorytransactionattributesourceadvisor Also have AnnotationTransactionAttributeSource and TransactionInterceptor these two items. bean Will be woven into the agent together.
So in In the process of Spring transaction initialization, four main transactions are registered bean:
-
InfrastructureAdvisorAutoProxyCreator
-
BeanFactoryTransactionAttributeSourceAdvisor
-
AnnotationTransactionAttributeSource
-
`TransactionInterceptor`
InfrastructureAdvisorAutoProxyCreator It is mainly used to find transaction annotations on methods, classes or interfaces @ Transactional, target if annotation exists bean Create the agent and put the other three bean Woven into the agent, while the other three bean It is mainly used to execute the target method and commit and rollback transactions.
BeanFactoryTransactionAttributeSourceAdvisor
About using BeanFactoryTransactionAttributeSourceAdvisor,AnnotationTransactionAttributeSource,TransactionInterceptor To execute the target method, commit the transaction and roll back the transaction, which will be analyzed in the next article.