One, casual chat

Another week has passed. Are you satisfied with this week’s harvest? I believe that has been reading my article partners know, Spring source intensive reading series article has written a lot of articles, today is still the same as before, we will analyze Spring for transaction management!

For those of you who have used Spring, you can configure or add a @Transactional annotation to a method to automatically manage JDBC operations, commit or roll back. Perhaps some of the students who have read part of the source code or have seen all similar articles should know that he is based on AOP to complete the management of transactions, but for its implementation details are not very clear, today the purpose of this article is to give you a thorough explanation of Spring for transaction encapsulation!

Ii. Brief introduction of principle

The whole fluency is simple to say very simple!

  1. Through annotation@EnableTransactionManagementImport aTransactionManagementConfigurationSelectorRegistry!
  2. TransactionManagementConfigurationSelectorInject two things into the Spring container, an AOP processor and an interceptor for transactions used by AOP!
  3. The injected AOP processor implements JDK or CGLIB dynamic proxies for classes, using transaction method interceptors to manage Spring transactions!

A total of three parts, let’s take a look at the details!

Three, source code lead

As we all know, Spring is one of the most popular source codes in the world. If you want to know more about the source code, you must be able to use Spring. We need to use to Spring’s transaction, need the corresponding configuration class or start on the class add an annotation called @ EnableTransactionManagement like the image below:

The whole Spring transaction problem is actually around this annotation. As usual, any Spring annotation called Enablexxxxxx uses @import to Import something shady. Of course, this transaction annotation is no exception. Of course the code involved inside is not I use pictures instead! Let’s go into this annotation and see!

Sure enough, indeed as expected use @ Import into a place called TransactionManagementConfigurationSelector, @ the role of the Import, interested readers can turn to articles, there is no need to do too much, Just remember that it can inject a class into Spring! Then it goes without saying that the point is there too! We enter into TransactionManagementConfigurationSelector source inside look at the specific logic!

Note that AdviceModeImportSelector is a subclass of ImportSelector and one of Spring’s built-in interfaces. Its role is to create a bean from an array of fully qualified names returned by selectImports().

Note that the parent selectImports() calls the selectImports(AdviceMode AdviceMode) method above, Through an array class this method returns the fully qualified name to create a bean, annotation @ EnableTransactionManagement default is to use the PROXY agent as the default mode, we this chapter also explains pattern as a PROXY! Shown above, when the type of the PROXY, returns the fully qualified name of the two classes: AutoProxyRegistrar, ProxyTransactionManagementConfiguration, let’s say AutoProxyRegistrar! Let’s go inside AutoProxyRegistrar to look at the source code logic!

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {....}
Copy the code

Suddenly found that he is a subclass of ImportBeanDefinitionRegistrar, read a few articles before probably know that this is our old friend, he is able to provide a callback, some originally not in scan logic of class registered bean into the Spring container. We look at registerBeanDefinitions() :

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {... Ignore unnecessary source code.....for(String annType : annTypes) { ..... Ignore unnecessary source code.....if (mode == AdviceMode.PROXY) {
                // If the proxy mode is used (the default), this is the object used to register a proxy logicAopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); . Ignore unnecessary source code..... }}}... Ignore unnecessary source code..... }Copy the code

Let’s go inside this method and see what’s registered! Enter the AopConfigUtils. RegisterAutoProxyCreatorIfNecessary (registry);

@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    // Empty shell method can enter
    return registerAutoProxyCreatorIfNecessary(registry, null);
}
Copy the code

Hey hey, nothing to say, continue to chase down!

@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
    / / a complete registration logic focused on InfrastructureAdvisorAutoProxyCreator object
    return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
Copy the code

Oh hoo, seemed to pass a InfrastructureAdvisorAutoProxyCreator class here, don’t know why, let’s say, you only need to remember, Spring to step in, this class is the key, we’ll say, now we go to see, how he take this class! We continue to enter registerOrEscalateApcAsRequired:

/ * * * this InfrastructureAdvisorAutoProxyCreator. This step is registered in the real class object * complete agent operation Our next step is to enter InfrastructureAdvisorAutoProxyCreator object inside look at what your completed operation *@paramCLS InfrastructureAdvisorAutoProxyCreator object *@paramRegistry registration tool *@paramSource Class source *@returnA converted BeanDefinition */
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<? > cls, BeanDefinitionRegistry registry,@Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // If the current bean factory already contains the transaction manager
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // Get the BeanDefinition
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // If the current BeanDefinition is different from the one that already exists in the bean factory, replace it with the current transaction manager
        if(! cls.getName().equals(apcDefinition.getBeanClassName())) {// Get the priority of the current bean
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            // Get the current bean priority
            int requiredPriority = findPriorityForClass(cls);
            // If the current bean priority is greater than an existing bean priority, replace it
            if(currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); }}// Do not perform injection directly return
        return null;
    }
    / / this step is the real want to Spring into a InfrastructureAdvisorAutoProxyCreator. The class object
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    / / set the source
    beanDefinition.setSource(source);
    // Set the priority
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // Register
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}
Copy the code

This step, the key see comments, obviously, the Spring on the step into the InfrastructureAdvisorAutoProxyCreator. Class into the Spring container, we continue to chase also no meaning, We now need to look back on InfrastructureAdvisorAutoProxyCreator this class, it is clear that the Spring injection, this class is definitely has a purpose, we go in and see! Into the injection InfrastructureAdvisorAutoProxyCreator class:

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

	@Nullable
	private ConfigurableListableBeanFactory beanFactory;


	@Override
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		super.initBeanFactory(beanFactory);
		this.beanFactory = beanFactory;
	}

	@Override
	protected boolean isEligibleAdvisorBean(String beanName) {
		return (this.beanFactory ! =null && this.beanFactory.containsBeanDefinition(beanName) &&
				this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE); }}Copy the code

To be honest, when I came in to look at it, I was confused, there was nothing in it, there was no keyword related to the transaction, but when I opened the class inheritance diagram, I immediately understood! Let’s take a look at its class diagram!

It is a subclass of Abstractauto Creator. If you don’t know what it is, then you can use AOP source code to explain what this class is. Is the processing agent, if you forget, I’ll give you back to the following, open @ EnableAspectJAutoProxy source, see he injected a AnnotationAwareAspectJAutoProxyCreator class, we have a look at his class diagram:

From previous studies, we can know that it is a subclass of BeanPostProcessor, which is the post-processor of beans belonging to Spring. We can also understand that Spring manages transactions through AOP, so it is clear at this point. He will call postProcessBeforeInstantiation () method to wrapper classes, plus agent logic!

We entered the AbstractAutoProxyCreator# postProcessAfterInitialization method to have a look at:

/** * If the bean is identified by a subclass as the bean to be propped, the proxy is created using the configured interceptor. *@see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        // Get the cached key here and fetch it from below.
        // Remember the level 3 cache that was introduced in order to solve the problem of circular dependencies. The level 2 cache can be solved, but the level 3 cache is used, and the level 3 cache still uses a factory
        / / org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory. GetEarlyBeanReference yes this method again
        // Use the factory to return the corresponding proxy object
        / / is called org. Springframework. Aop) framework. Autoproxy. AbstractAutoProxyCreator. GetEarlyBeanReference
        // Cache a copy of your own object
        // After the dynamic proxy is returned by the third level cache, there is no AOP logic to return the beans already processed by the third level cache
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // Check whether the previously stored AOP proxy class is inconsistent with the created bean. If so, the bean is already a proxy class and no further AOP logic is required
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Execute the wrapping logic of an AOP proxy if it is judged that a proxy is needed
            returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

Familiar with not, this is before the Spring Aop source analysis of the first method call, the subsequent call logic is consistent, here do not do too much duplicity, the details into the [angry! Interviewer you come over, I give you a handwritten Spring Aop implementation! Watch!

Actually, so far, at least we know that the question of the Spring by @ Import injected a InfrastructureAdvisorAutoProxyCreator, this class is a rear the processor, able to deal with the logic of SpringAOP, at least we know it, Our corresponding class can be managed by AOP, but where are the transactions actually done?

At this point, we need to look @ the Import into another class ProxyTransactionManagementConfiguration!

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	/** * Transaction register resolver *@returnReturns the library registry parser */
	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(a) {
		// Build a notification class
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		// Attributes of transaction-related annotations
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		// Set transaction interceptor
		advisor.setAdvice(transactionInterceptor());
		if (this.enableTx ! =null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource(a) {
		return new AnnotationTransactionAttributeSource();
	}

	/** * transaction interceptor *@returnReturns a transaction interceptor */
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(a) {
		// This step is where the actual transaction interceptor will eventually enter to perform DB transaction management
		/ / this class are wrapped in BeanFactoryTransactionAttributeSourceAdvisor setAdvice
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager ! =null) {
			interceptor.setTransactionManager(this.txManager);
		}
		returninterceptor; }}Copy the code

Ok, now we know the following:

  1. There is a special post-processor in the Spring container:AbstractAutoProxyCreator, which wraps the Service into an enhanced class!
  2. There is a special method interceptor in the Spring container:TransactionInterceptor, it can manage the transaction of the corresponding method!

Let’s explore how AOP’s processor and method interceptors relate!

Some specific method call logic, I am angry! Interviewer you come, I will give you a handwritten Spring Aop implementation! Write very detailed, interested partners can take a look at this article only transaction related logic to explain!

We entered the AOP processor: AbstractAutoProxyCreator# postProcessAfterInitialization inside:

/** * If the bean is identified by a subclass as the bean to be propped, the proxy is created using the configured interceptor. *@see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        // Get the cached key here and fetch it from below.
        // Remember the level 3 cache that was introduced in order to solve the problem of circular dependencies. The level 2 cache can be solved, but the level 3 cache is used, and the level 3 cache still uses a factory
        / / org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory. GetEarlyBeanReference yes this method again
        // Use the factory to return the corresponding proxy object
        / / is called org. Springframework. Aop) framework. Autoproxy. AbstractAutoProxyCreator. GetEarlyBeanReference
        // Cache a copy of your own object
        // After the dynamic proxy is returned by the third level cache, there is no AOP logic to return the beans already processed by the third level cache
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // Check whether the previously stored AOP proxy class is inconsistent with the created bean. If so, the bean is already a proxy class and no further AOP logic is required
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Execute the wrapping logic of an AOP proxy if it is judged that a proxy is needed
            returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

Enter the wrapIfNecessary(XXXXX) method:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {... Ignore unwanted methods...// Find the corresponding AOP proxy to find the pointcut of this bean
        // How did he find the corresponding cut point of the bean?
        // Get all applicable advisors for the current object. Find all classes whose pointcuts are their corresponding @aspect annotations
        // It mainly uses the first step to get all the aspect methods, namely the class of type advisor.class
        // Use the current class in a loop to determine whether to use the current class
        // Add to array if applicable, next if not!
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if(specificInterceptors ! = DO_NOT_PROXY) {// If the proxy is allowed
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // This step is the main logic, creating a proxy object with the following parameters: name of the class object bean proxy class information (location, pointcut, etc.) bean object
        Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, newSingletonTargetSource(bean)); . Ignore unwanted methods...returnproxy; }... Ignore unwanted methods...// Return the original Bean object
        return bean;
}
Copy the code

As shown above, you can see that the method interceptor has been found here! Let’s go further, pass the method interceptor we found to createProxy(XXXX), and go inside:

protected Object createProxy(Class<? > beanClass,@Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {... Ignore unwanted methods...// Create a proxy factory
        ProxyFactory proxyFactory = new ProxyFactory();
    // Package agent information cut point information packaging
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.setFrozen(this.freezeProxy); . Ignore unwanted methods...// Real proxy logic here is mainly to get a real proxy argument is the class loader
    return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code

Then go to getProxy(getProxyClassLoader()); Methods:

/** * Create a new agent based on the Settings in this factory. * <p> can be called repeatedly. If we added interfaces that would be different or removed. Interceptors can be added and removed. * <p> Use the given class loader (if a proxy needs to be created). *@paramClassLoader to create a proxy (or {@codeNull} is the default value for low-level proxy tools) *@returnThe proxy object */
public Object getProxy(@Nullable ClassLoader classLoader) {
    //createAopProxy returns the proxy type to be used. Note that this is passed into this method
    //getProxy creates a proxy object using the returned proxy type
    return createAopProxy().getProxy(classLoader);
}
Copy the code

This logic will not say more, in [angry! Interviewer you come, I will give you a handwritten Spring Aop implementation! As described in detail in this article, we go directly to getProxy(classLoader); Method, because we are using the JDK dynamic proxy, we will end up in the JdkDynamicAopProxy#getProxy(java.lang.classloader) method!

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // Invoke JDK native proxy logic
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

The above method is particularly important, and it’s the core of the whole JDK dynamic proxy, and we can see that the final incoming InvocationHandler is this and this represents the final callback method that the JDK dynamic proxy executes, invoker, which is inside this class, and we go into the invoker method of this class, Here is the complete final method of calling the transaction method interceptor!

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {... Ignore unwanted methods...// Get the interceptor chain for this method.
    // This interceptor chain is all the pointcut methods available to the corresponding bean
    // Here is the responsibility chain for all the notification classes filtered out above
    / / org/springframework/aop/framework/autoproxy AbstractAutoProxyCreator. Java: injection in 366
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    if(chain.isempty ()) {... Ignore unwanted methods... }else {
        // We need to create a method call...
        MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // Enter the join point through the interceptor chain. This is the main methodretVal = invocation.proceed(); }... Ignore unwanted methods... }Copy the code

Let’s take a look at the elements in the interceptor chain:

As you can see, there is only one interceptor in the interceptor, which is a method interceptor injected by the previous transaction annotation.

The invocation. Proceed () method:

@Override
@Nullable
public Object proceed(a) throws Throwable {
    // We start with index -1 and increment ahead of time.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // Start executing the real target method when the chain is completely called
        // Note that this is important, why?
        // Since all interceptors are registered in the call chain during the rebuild, the chain of responsibility design pattern is adopted
        // The call chain will be passed to the method of each step, and the target method will not be called until the call chain is complete
        // Instead, the method represented in the call chain executes the next pointcut interceptor
        // This step is the actual method executed after the actual chain is called
        // Then this method must be the one that calls the target method
        return invokeJoinpoint();
    }
    / / interceptorsAndDynamicMethodMatchers here is coming the tangent point of approach The bean corresponding to several tangent point there will be a few interceptors
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceofInterceptorAndDynamicMethodMatcher) {... Ignore unwanted methods... }else {
        // It is an interceptor, so we just call it: the pointcut is statically evaluated before the object is constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code

This method in the AOP source code is also extremely important, but is not the focus of this lesson, interested partners can refer to: [angry! Interviewer you come, I will give you a handwritten Spring AOP implementation!

In fact the above code will go into the else, is invoked (MethodInterceptor) interceptorOrInterceptionAdvice) invoke (this); Method, we take a look at this interceptorOrInterceptionAdvice actual type:

Yes, it will eventually enter the TransactionInterceptor#invoker method; At this point, the AOP handler is completely tied to the transaction method interceptor, which we enter (note the this in the argument) :

We’re going to go to invokeWithinTransaction, and notice that it’s passing a callback to this method, and if you don’t understand Java8 you can look at the comment, this method callback is going to go back to the previous logic, that’s all later, we’re going to go inside the method, and notice this, it says:

@Nullable
protected Object invokeWithinTransaction(Method method, @NullableClass<? > targetClass,final InvocationCallback invocation) throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.TransactionAttributeSource tas = getTransactionAttributeSource(); . Ignore unwanted methods...if (txAttr == null| |! (tminstanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction partitioning using getTransaction and COMMIT/ROLLBACK calls.
        // Enable transaction.
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        // The target method returns a value
        Object retVal;
        try {
            // This is a suggestion: call the next interceptor in the chain.
            // Normally, this will cause the target object to be called.
            // This is to go back and get the final return value after the processing is complete
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // If the target method throws an exception, the transaction is rolled back here
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // Burial affairs
            cleanupTransactionInfo(txInfo);
        }
        // Commit the transaction after return
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else {
        。。。。。。。。。忽略不需要的方法。。。。。。。。。。。
    }
}
Copy the code

This is where the method is finally committed and rolled back, and the method call inside the try is actually called back to the callback function passed in earlier!

Will call to, the reason is in the call (MethodInterceptor) interceptorOrInterceptionAdvice) invoke (this); Is passed a this!

Yes, it will be called back, and finally complete an interception of the method, thus completing the transaction of the proxy!

Good this period of the transaction related to the source analysis on the introduction of the end, what doubts or other aspects of the private chat or message author oh!

Four, Xu Huang a gun to wave summary

Ha ha, according to the previous logic, how can I not draw a summary of the old emperor! I’ve summarized the previous source logic in the following figure, which I hope will help you:

【 Recommended reading 】

  1. How did MyBatis conquer Spring?
  2. The @Configuration class is invisible in Spring
  3. Angry! Interviewer you come over, I will give you a handwritten Implementation of Spring Aop
  4. Swastika long text to help you deeply navigate the Spring cycle to rely on source code implementation
  5. I heard that you are confused when you read the Spring source code? I’ll set up the shelves for you. You fill them out

If the understanding of the article is wrong, welcome big men private chat correction! Welcome to pay attention to the author’s public number, progress together, learn together!