1. Introduction

In the process of system application development, transactions are usually needed to ensure the consistency of business data. The implementation methods are as follows: The standard implementation of starting transactions, performing database writes, committing or rolling back transactions is suitable for a small number of consistent services. If there are a large number of services that need to ensure data consistency, it will not only make developers code repeatedly, but also cause redundant code to the system. Based on these issues, the great Spring framework provides us with the @Transactional annotation, so how does it solve our problems with a single annotation? How do we go about analyzing this?

SpringBoot integration often starts with an xxxAutoConfiguration

2. Automatic configuration

Open TransactionAutoConfiguration automatic configuration class can see a more important annotation @ EnableTransactionManagement for open transaction management functions, @ EnableTransactionManagement annotations and imported the AutoProxyRegistrar and ProxyTransactionManagementConfiguration

2.1 Transaction Configuration

ProxyTransactionManagementConfiguration BeanFactoryTransactionAttributeSourceAdvisor declares a tangent plane, See section will have a corresponding tangent point TransactionAttributeSourcePointcut (used to declare cuts range), and notify TransactionInterceptor (used to implement the target of the subsequent operation).

2.2 the statement@TransactionalAnnotation processor

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

AnnotationTransactionAttributeSource instantiation specifies the annotation parser for SpringTransactionAnnotationParser

You can see that this parser is primarily used to handle the @Transactional annotation

2.3 Injecting automatic proxy registrars

In 2. Automatic configuration mentioned @ EnableTransactionManagement also introduces AutoProxyRegistrar, to inject InfrastructureAdvisorAutoProxyCreator IOC container

@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
      BeanDefinitionRegistry registry, @Nullable Object source) {
   return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
Copy the code

InfrastructureAdvisorAutoProxyCreator implements the BeanPostProcessor interface, has the ability of interception and processing Bean

2.3.1 BeanPost processing

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

2.3.2 Get all from the containerAdvisor

/**
  * Find all candidate Advisors to use in auto-proxying.
  * @return the List of candidate Advisors
  */
protected List<Advisor> findCandidateAdvisors(a) {
    Assert.state(this.advisorRetrievalHelper ! =null."No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}
Copy the code

2.3.3 Screening the qualified onesAdvisor

public static boolean canApply(Pointcut pc, Class<? > targetClass,boolean hasIntroductions) {
    // 1. Get the corresponding MethodMatcher for the pointcut
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    for(Class<? > clazz : classes) {// 2. Get all the methods in the current class
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            / / 3. Determine whether conform to the requirements of the tangent point, the methodMatcher for TransactionAttributeSourcePointcut here
            if (methodMatcher.matches(method, targetClass)) {
                return true; }}}return false;
}
Copy the code

Whether have @ Transactional annotation on judgment method, if there is then used to parse and generate SpringTransactionAnnotationParser TransactionAttribute

2.3.4 AdvisorThe sorting

sortAdvisors(eligibleAdvisors);
Copy the code

2.3.4 summary

protected List<Advisor> findEligibleAdvisors(Class
        beanClass, String beanName) {
    // 1. Get all cuts of type Advisor from the container
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2. Filter out sections that qualify (i.e. marked with @Transactional annotation ona class or method)
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if(! eligibleAdvisors.isEmpty()) {// 3. Sort the matching sections in ascending order
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}
Copy the code

2.4 Selecting a proxy method

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if(! IN_NATIVE_IMAGE && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { Class<? > targetClass = config.getTargetClass();// 1. If the interface is implemented, select JDK proxy
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 2. Select cglib proxy
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return newJdkDynamicAopProxy(config); }}Copy the code

2.4.1 Generating proxies

Cglib is used as an example

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    try{ Class<? > rootClass =this.advised.getTargetClass(); Class<? > proxySuperClass = rootClass;// Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if(classLoader ! =null) {
            enhancer.setClassLoader(classLoader);
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); Callback[] callbacks = getCallbacks(rootClass); }}Copy the code
privateCallback[] getCallbacks(Class<? > rootClass)throws Exception {
    // Choose an "aop" interceptor (used for AOP calls).
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
}
Copy the code

Create Enhancer and specify the callback for DynamicAdvisedInterceptor

2.5 Invoking the Proxy

Execute the proxied object target method userservice.saveUser (user); When calling DynamicAdvisedInterceptor intercept () method

2.5.1 Filter those that meet the conditionsAdvice

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    Advised config, Method method, @NullableClass<? > targetClass) {
    // 1. Iterate over all advisors that meet the criteria, that is, the advisors returned in Section 2.3.3
    for (Advisor advisor : advisors) {
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                // 2. Determine whether the pointcut requirements are met
                boolean match = mm.matches(method, actualClass);
                // 3. Meet the pointcut requirements
                if (match) {
                    // 3. Get the corresponding notification, that is, TransactionInterceptorMethodInterceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); }}}}// 4. Returns a notification that satisfies the condition
    return interceptorList;
}
Copy the code

2.5.2 Perform operations that meet the conditionsAdvice

@Override
@Nullable
public Object proceed(a) throws Throwable {
	// 1. If there is no advice to execute, execute the target method
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // 2. Retrieve an advice from the advice list
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 3. Execute advice invoke, that is, TransactionInterceptor invoke
	return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
Copy the code

3. Transaction invocation

3.1 Obtaining transaction configuration properties

TransactionAttributeSource tas = getTransactionAttributeSource();
finalTransactionAttribute txAttr = (tas ! =null ? tas.getTransactionAttribute(method, targetClass) : null);
Copy the code

This is the @Transactional annotation’s declared property

3.2 Obtaining the transaction manager

final TransactionManager tm = determineTransactionManager(txAttr);
Copy the code

From the container to get DataSourceTransactionManagerAutoConfiguration automatic configuration classes declared in the transaction manager JdbcTransactionManager

public class DataSourceTransactionManagerAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(TransactionManager.class)
    JdbcTransactionManager transactionManager(DataSource dataSource, ObjectProvider
       
         transactionManagerCustomizers)
        {
        JdbcTransactionManager transactionManager = new JdbcTransactionManager(dataSource);
        transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
        returntransactionManager; }}Copy the code

3.3 Executing transactions

4. To summarize

Transactional implements the three elements of section, pointcut, and notification

  • InfrastructureAdvisorAutoProxyCreatorThe rear processor intercepts everythingBean
  • Traversal all types asAdvisorthesection
  • Return to meetPoint of tangencyConditions of thesectionThe list of
  • Select proxy method
  • Generated proxy
  • callnoticetheinvoke()methods
    • Open the transaction
    • Call othernoticetheinvoke()Method if the target method is not executed
    • Perform an exception to roll back the transaction
    • Execute successfully, commit transaction
  • Implement target method

Understanding the @Transactional annotation not only gives you a clear understanding of aspects, pointcuts, and notifications, but also allows you to implement functions such as the @Cache annotation for application caching and the @Async annotation for asynchronous business execution