Spring Boot source code analysis @EnableTransactionManagement

Related Reading

TransactionAutoConfiguration

Spring framework provides @Transactionalannotations to support annotation-driven transaction management;
Spring Boot framework enables annotation-driven transaction management by default, that is, throughTransactionAutoConfigurationTo achieve automatic configuration of transaction management, the code is as follows:

@Configuration 
@ConditionalOnClass ( PlatformTransactionManager . class ) 
@AutoConfigureAfter ( { JtaAutoConfiguration . class , HibernateJpaAutoConfiguration . class , 
        DataSourceTransactionManagerAutoConfiguration . class , 
        Neo4jDataAutoConfiguration . class  } ) 
@EnableConfigurationProperties ( TransactionProperties . class ) 
public  class  TransactionAutoConfiguration  {

    @Bean 
    @ConditionalOnMissingBean 
    public TransactionManagerCustomizers platformTransactionManagerCustomizers ( 
            ObjectProvider < List < PlatformTransactionManagerCustomizer < ? >>> customizers )  { 
        return  new  TransactionManagerCustomizers ( customizers . getIfAvailable ( ) ) ; 
    }

    @Configuration 
    @ConditionalOnSingleCandidate ( PlatformTransactionManager . class ) 
    public  static  class  TransactionTemplateConfiguration  {

        private  final PlatformTransactionManager transactionManager ;

        public  TransactionTemplateConfiguration ( 
                PlatformTransactionManager transactionManager )  { 
            this . transactionManager = transactionManager ; 
        }

        @Bean 
        @ConditionalOnMissingBean 
        public TransactionTemplate transactionTemplate ( )  { 
            return  new  TransactionTemplate ( this . transactionManager ) ; 
        }

    }

    @Configuration 
    @ConditionalOnBean ( PlatformTransactionManager . class ) 
    @ConditionalOnMissingBean ( AbstractTransactionManagementConfiguration . class ) 
    public  static  class  EnableTransactionManagementConfiguration  {

        @Configuration 
        @EnableTransactionManagement ( proxyTargetClass =  false ) 
        @ConditionalOnProperty ( prefix =  "spring.aop" , name =  "proxy-target-class" , havingValue =  "false" , matchIfMissing =  false ) 
        public  static  class  JdkDynamicAutoProxyConfiguration  { 
        }

        @Configuration 
        @EnableTransactionManagement ( proxyTargetClass =  true ) 
        // takes effect when the spring.aop.proxy-target-class property value is true or does not exist 
        @ConditionalOnProperty ( prefix =  "spring.aop" , name =  "proxy-target-class" , havingValue =  "true" , ​​matchIfMissing =  true ) 
        public  static  class  CglibAutoProxyConfiguration  { 
        } 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

according toTransactionAutoConfigurationThe configuration information can be seen, when the introductionPlatformTransactionManager(provided by spring-tx), the configuration class takes effect automatically;
DataSourceTransactionManagerAutoConfigurationwill createTransactionManager, if currently existsAbstractTransactionManagementConfiguration,Right nowProxyTransactionManagementConfiguration, then it means that @ has been introducedEnableTransactionManagementannotation(ProxyTransactionManagementConfigurationwill only be @EnableTransactionManagementannotation introduction), soEnableTransactionManagementConfigurationDoes not need to take effect; otherwise it will take effect, thereby introducing @EnableTransactionManagementannotation;
EnableTransactionManagementConfigurationeffective unless specifiedspring.aop.proxy-target-classThe configuration item is false, at this timeJdkDynamicAutoProxyConfigurationwill take effect, otherwiseCglibAutoProxyConfigurationTake effect, both configurations will introduce @EnableTransactionManagementAnnotation, just annotatedproxyTargetClassIf the attribute value is different, the annotation will enable transaction management, which is actually the introduction ofProxyTransactionManagementConfiguration;

@EnableTransactionManagement

source code

@Target ( ElementType . TYPE ) 
@Retention ( RetentionPolicy . RUNTIME ) 
@Documented 
@Import ( TransactionManagementConfigurationSelector . class ) 
public @ interface  EnableTransactionManagement  {

    // Proxy mode: CGLIB or JDK Interface 
    // The default is false, which means based on JDK Interface 
    // If set to true, it will affect all Spring-managed beans that need to be proxied 
    boolean  proxyTargetClass ( )  default  false ;

    // Proxy application mode: Proxy or AspectJ 
    // Proxy mode only allows calls to be intercepted through a proxy, not local calls in the same class 
    // In AspectJ mode, proxyTargetClass() is invalid and will intercept local calls in the same class 
    AdviceMode mode ( )  default AdviceMode . PROXY ;

    // When multiple recommendations are applied to a particular join point, the order in which transaction operations are performed 
    int  order ( )  default Ordered . LOWEST_PRECEDENCE ;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Brief analysis

Through the Import mechanism, introduceTransactionManagementConfigurationSelector;

@Import ( TransactionManagementConfigurationSelector.class ) _ _
  • 1

TransactionManagementConfigurationSelectorinherited fromAdviceModeImportSelector,AdviceModeImportSelectorImplementation supportAdviceModeAlgorithm template, and supports generic @EnableXxxAnnotation mode, the code is as follows:

public  final String [ ]  selectImports ( AnnotationMetadata importingClassMetadata )  { 
    // Get the annotation types supported by this class
     // EnableTransactionManagement 
    Class for TransactionManagementConfigurationSelector < ? > annType = GenericTypeResolver . resolveTypeArgument ( getClass ( ) , AdviceModeImportSelector . class ) ; 
    Assert . state ( annType != null , "Unresolvable type argument for AdviceModeImportSelector" ) ;

    // Get annotation attribute data 
    AnnotationAttributes attributes = AnnotationConfigUtils . attributesFor ( importingClassMetadata , annType ) ; 
    if  ( attributes == null )  { 
        throw  new  IllegalArgumentException ( String . format ( 
            "@%s is not present on importing class '%s' as expected " , 
            annType . getSimpleName ( ) , importingClassMetadata . getClassName ( ) )) ; 
    }

    // Get the AdviceMode attribute value of the annotation 
    AdviceMode adviceMode = attributes . getEnum ( this . getAdviceModeAttributeName ( ) ) ; 
    // Get the import configuration class information according to AdviceMode 
    // This method is implemented by subclasses of algorithm details 
    String [ ] imports =  selectImports ( adviceMode ) ; 
    if  ( imports == null )  { 
        throw  new  IllegalArgumentException ( String . format ( "Unknown AdviceMode: '%s'" ,adviceMode ) ) ; 
    } 
    return imports ; 
}

@Nullable 
protected  abstract String [ ]  selectImports ( AdviceMode adviceMode ) ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

TransactionManagementConfigurationSelectorImplemented algorithm detailsselectImports,code show as below:

protected String [ ]  selectImports ( AdviceMode adviceMode )  { 
    switch  ( adviceMode )  { 
        case PROXY : 
            return  new  String [ ]  { AutoProxyRegistrar . class . getName ( ) , ProxyTransactionManagementConfiguration . class . getName ( ) } ; 
        case ASPECTJ : 
            return  new  String [ ] { TransactionManagementConfigUtils . TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME } ; 
        default : 
            return null ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

according to@EnableTransactionManagement(proxyTargetClass = true)configuration will importAutoProxyRegistrarandProxyTransactionManagementConfiguration;

AutoProxyRegistrar

AutoProxyRegistrarAchievedImportBeanDefinitionRegistrar, used to register the automatic proxy creator with the containerAutoProxyCreator,code show as below:

/**
 * importingClassMetadata : The metadata information of the configuration class imported through Import, in this case the configuration class is CglibAutoProxyConfiguration
 * registry: Bean definition container
 */ 
public  void  registerBeanDefinitions ( AnnotationMetadata importingClassMetadata , BeanDefinitionRegistry registry )  { 
    boolean candidateFound =  false ; 
    // Get the annotation list of the configuration class 
    // In this example, the annotations are: 
    // @Configuration 
    // @EnableTransactionManagement 
    // @ConditionalOnProperty 
    Set < String > annoTypes = importingClassMetadata . getAnnotationTypes ( ) ; 
    // Traverse the annotation list 
    for  ( String annoType : annoTypes)  { 
        // Get annotation attribute data 
        AnnotationAttributes candidate = AnnotationConfigUtils . attributesFor ( importingClassMetadata , annoType ) ; 
        if  ( candidate == null )  { 
            continue ; 
        } 
        // Get mode attribute value 
        Object mode = candidate . get ( "mode" ) ; 
        // Get proxyTargetClass property value 
        Object proxyTargetClass = candidate . get ( "proxyTargetClass") ; 
        // filter out @EnableTransactionManagement annotations 
        if  ( mode != null && proxyTargetClass != null && AdviceMode . class  == mode . getClass ( )  && 
                Boolean . class  == proxyTargetClass . getClass ( ) )  { 
            candidateFound =  true ; 
            // In this example, mode is AdviceMode.PROXY and proxyTargetClass is true 
            if  ( mode == AdviceMode.PROXY )  { AopConfigUtils 
                .registerAutoProxyCreatorIfNecessary ( registry ) ; if ( ( Boolean ) proxyTargetClass ) { // Use subclass- based proxy 
                    AopConfigUtils . forceAutoProxyCreatorToUseClassProxying ( registry ) ; return ; } } } } if ( ! candidateFound && logger . isWarnEnabled ( ) )
                  
                    
                    
                
            
        
    
      { 
        // Exception LOG record 
        String name =  getClass ( ) . getSimpleName ( ) ; 
        logger . warn ( 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 ) ) ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

The core logic isAopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);andAopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);,code show as below:

public  static BeanDefinition registerAutoProxyCreatorIfNecessary ( BeanDefinitionRegistry registry )  { 
    return  registerAutoProxyCreatorIfNecessary ( registry , null ) ; 
}

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" ) ; 

    // Determine if there is an automatic proxy creator Bean definition in the current container 
    if  ( registry . containsBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME ) )  { 
        // Get the existing Bean Definition 
        BeanDefinition apcDefinition = registry . getBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME ) ; 
        // Determine the existing Bean Whether the defined className is consistent with the current one 
        if  ( ! cls . getName ( ) . equals ( apcDefinition . getBeanClassName ( ) ) )  { 
            // If not, use the higher priority
            int currentPriority =  findPriorityForClass ( apcDefinition . getBeanClassName ( ) ) ; 
            int requiredPriority =  findPriorityForClass ( cls ) ; 
            if  ( currentPriority < requiredPriority )  { 
                apcDefinition . setBeanClassName ( cls . getName ( ) ) ; 
            } 
        } 
        return null ; 
    }

    // Create a bean definition based on the current class and put it into the container 
    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 ; 
}

public  static  void  forceAutoProxyCreatorToUseClassProxying ( BeanDefinitionRegistry registry )  { 
    if  ( registry . containsBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME ) )  { 
        BeanDefinition 
        definition = registry . _ _ _ _ _ 
        _ _ _ _ _ _ _ _ _ _.TRUE ) ; 
    } 
} _
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfigurationis a configuration class that inherits fromAbstractTransactionManagementConfiguration;
AbstractTransactionManagementConfigurationAchievedImportAwareinterface, will inject @EnableTransactionManagementAnnotate attribute data and inject at the same timeTransactionManagementConfigurerConfiguration, the code is as follows:

public  void  setImportMetadata ( AnnotationMetadata importMetadata )  { 
    // Get @EnableTransactionManagement annotation attribute data 
    this . enableTx = AnnotationAttributes . fromMap ( 
            importMetadata . getAnnotationAttributes ( EnableTransactionManagement . class . getName ( ) ,  false ) ) ; 
    if  ( this . enableTx == null )  { 
        throw  new IllegalArgumentException ( 
                "@EnableTransactionManagement is not present on importing class "  + importMetadata . getClassName ( ) ) ; 
    } 
}

@Autowired ( required =  false ) 
void  setConfigurers ( Collection < TransactionManagementConfigurer > configurers )  { 
    if  ( CollectionUtils . isEmpty ( configurers ) )  { 
        return ; 
    } 
    // // TransactionManagementConfigurer bean can only have one 
    if  ( configurers . size ( )  >  1 )  { 
        throw  new  IllegalStateException ("Only one TransactionManagementConfigurer may exist" ) ; 
    } 
    TransactionManagementConfigurer configurer = configurers . iterator ( ) . next ( ) ; 
    // use a custom transaction manager 
    this . txManager = configurer . annotationDrivenTransactionManager ( ) ; 
}

@Bean ( name = TransactionManagementConfigUtils . TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME ) 
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE ) 
public TransactionalEventListenerFactory transactionalEventListenerFactory ( )  { 
    //Inject the listener factory, support methods annotated with @TransactionalEventListener to listen for transaction-related events 
    return  new  TransactionalEventListenerFactory ( ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

ProxyTransactionManagementConfigurationwill be injected into the containerBeanFactoryTransactionAttributeSourceAdvisor,TransactionAttributeSource,TransactionInterceptor,code show as below:

@Bean ( name = TransactionManagementConfigUtils . TRANSACTION_ADVISOR_BEAN_NAME ) 
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE ) 
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor ( )  { 
    // Transaction management core Advisor 
    BeanFactoryTransactionAttributeSourceAdvisor advisor =  new  BeanFactoryTransactionAttributeSourceAdvisor ( ) ; 
    advisor . setTransactionAttributeSource ( transactionAttributeSource ( ) ); 
    // Set enhancement 
    advisor . setAdvice ( transactionInterceptor ( ) ) ; 
    if  ( this . enableTx != null )  { 
        advisor . setOrder ( this . enableTx . < Integer > getNumber ( "order" ) ) ; 
    } 
    return advisor ; 
}

@Bean 
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE ) 
public TransactionAttributeSource transactionAttributeSource ( )  { 
    // Annotation-based transaction attribute source 
    return  new  AnnotationTransactionAttributeSource ( ) ; 
}

@Bean 
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE ) 
public TransactionInterceptor transactionInterceptor ( )  { 
    // transaction interceptor 
    TransactionInterceptor interceptor =  new  TransactionInterceptor ( ) ; 
    interceptor . setTransactionAttributeSource ( transactionAttributeSource ( ) ) ; 
    if  ( this . txManager != null )  { 
        // set Transaction manager 
        interceptor .setTransactionManager ( this . txManager ) ; 
    } 
    return interceptor ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

AnnotationTransactionAttributeSource

AnnotationTransactionAttributeSourceIs an annotation-based transaction attribute source, internally maintainedTransactionAnnotationParserCollection, used to parse the transaction attributes of a class or method, the code is as follows:

public  AnnotationTransactionAttributeSource ( )  { 
    // By default only public methods can be resolved 
    this ( true ) ; 
}

public  AnnotationTransactionAttributeSource ( boolean publicMethodsOnly )  { 
    this . publicMethodsOnly = publicMethodsOnly ; 
    this . annotationParsers =  new  LinkedHashSet < > ( 2 ) ; 
    // Holds SpringTransactionAnnotationParser by default to support Spring transaction annotations 
    this . annotationParsers . add ( new  SpringTransactionAnnotationParser ( ) ) ; 
    if  ( jta12Present )  {
        // Hold JtaTransactionAnnotationParser to support JTS transaction annotations 
        this . annotationParsers . add ( new  JtaTransactionAnnotationParser ( ) ) ; 
    } 
    if  ( ejb3Present )  { 
        // Hold Ejb3TransactionAnnotationParser to support EJB3 transaction annotations 
        this . annotationParsers . add ( new  Ejb3TransactionAnnotationParser ( ) ) ; 
    } 
}

protected TransactionAttribute findTransactionAttribute ( Method method )  { 
    return  determineTransactionAttribute ( method ) ; 
}

protected TransactionAttribute findTransactionAttribute ( Class < ? > clazz )  { 
    return  determineTransactionAttribute ( clazz ) ; 
}

protected TransactionAttribute determineTransactionAttribute ( AnnotatedElement ae )  { 
    // Traverse the internal transaction annotation parser 
    for  ( TransactionAnnotationParser annotationParser :  this . annotationParsers )  { 
        // Parse the transaction attribute 
        TransactionAttribute attr = annotationParser . parseTransactionAnnotation ( ae ) ; 
        if  ( attr != null )  { 
            // If the parsing is successful, return directly to 
            return attr ; 
        } 
    } 
    returnnull ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

SpringTransactionAnnotationParser

SpringTransactionAnnotationParserAchievedTransactionAnnotationParserInterface, responsible for parsing @TransactionalNote, the core code is as follows:

public  boolean  isCandidateClass ( Class < ? > targetClass )  { 
    // Whether the specified class may be annotated with @Transactional annotation 
    return AnnotationUtils . isCandidateClass ( targetClass , Transactional . class ) ; 
}

public TransactionAttribute parseTransactionAnnotation ( AnnotatedElement element )  { 
    // Find whether the element (method or class) is marked with @Transactional annotation 
    AnnotationAttributes attributes = AnnotatedElementUtils . findMergedAnnotationAttributes ( 
            element , Transactional . class ,  false ,  false ) ; 
    if  ( attributes != null )  { 
        // If it exists, continue to parse the @Transactional annotation attribute 
        return  parseTransactionAnnotation ( attributes ); 
    } 
    else  { 
        return null ; 
    } 
}

public TransactionAttribute parseTransactionAnnotation ( Transactional ann )  { 
    return  parseTransactionAnnotation ( AnnotationUtils . getAnnotationAttributes ( ann ,  false ,  false ) ) ; 
}

/**
 * Further parsing @Transactional annotation attributes
 */ 
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 ( ) ) ; 
    String timeoutString = attributes . getString ( "timeoutString" ) ; 
    Assert . isTrue ( ! StringUtils . hasText ( timeoutString )  || rbta . getTimeout ( )  <  0 , 
            "Specify 'timeout' or 'timeoutString', not both") ; 
    rbta .setTimeoutString ( timeoutString ) ; _

    rbta . setReadOnly ( attributes . getBoolean ( "readOnly" ) ) ; 
    rbta . setQualifier ( attributes . getString ( "value" ) ) ; 
    rbta . setLabels ( Arrays . asList ( attributes . getStringArray ( "label" ) ) ) ;

    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 ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

TransactionInterceptor

TransactionInterceptorinherited fromTransactionAspectSupport, and realizedMethodInterceptor,code show as below:

public Object invoke ( MethodInvocation invocation )  throws Throwable { 
    Class < ? > targetClass =  ( invocation . getThis ( )  != null ? AopUtils . getTargetClass ( invocation . getThis ( ) )  : null ) ;

    return  invokeWithinTransaction ( invocation . getMethod ( ) , targetClass ,  new  CoroutinesInvocationCallback ( )  { 
        @Override 
        @Nullable 
        public Object proceedWithInvocation ( )  throws Throwable { 
            return invocation . proceed ( ) ; 
        } 
        @Override 
        public Object getTarget ( )  { 
            return invocation . getThis( ) ; 
        } 
        @Override 
        public Object [ ]  getArguments ( )  { 
            return invocation . getArguments ( ) ; 
        } 
    } ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

The core processing logic of transaction management is in the parent classTransactionAspectSupport,code show as below:

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

    // Get the current transaction attribute source 
    TransactionAttributeSource tas =  getTransactionAttributeSource ( ) ; 
    // Get the transaction attribute of the current method 
    final TransactionAttribute txAttr =  ( tas != null ? tas . getTransactionAttribute ( method , targetClass )  : null ) ; 
    // Get the transaction manager 
    final TransactionManager tm =  determineTransactionManager ( txAttr ) ;

    if  ( this . reactiveAdapterRegistry != null && tm instanceof  ReactiveTransactionManager )  { 
        boolean isSuspendingFunction = KotlinDetector . isSuspendingFunction ( method ) ; 
        boolean hasSuspendingFlowReturnType = isSuspendingFunction && 
                COROUTINES_FLOW_CLASS_NAME . equals ( new  MethodParameter ( method ,  - 1 ) .getParameterType ( ) . getName ( ) ) ; 
        if  ( isSuspendingFunction &&  ! ( invocation instanceof  CoroutinesInvocationCallback ) )  { 
            throw  new  IllegalStateException ( "Coroutines invocation not supported: "  + method ) ; 
        } 
        CoroutinesInvocationCallback corInv =  ( isSuspendingFunction ?  ( CoroutinesInvocationCallback ) invocation : null) ;

        ReactiveTransactionSupport txSupport =  this . transactionSupportCache . computeIfAbsent ( method , key - >  { 
            Class < ? > reactiveType = 
                    ( isSuspendingFunction ?  ( hasSuspendingFlowReturnType ? Flux . class  : Mono . class )  : method . getReturnType ( ) ) ; 
            ReactiveAdapter adapter = this . reactiveAdapterRegistry . getAdapter ( reactiveType ) ; 
            if  ( adapter == null )  { 
                throw  new  IllegalStateException ( "Cannot apply reactive transaction to non-reactive return type: "  + 
                        method . getReturnType ( ) ) ; 
            } 
            return  new  ReactiveTransactionSupport ( adapter ) ; 
        } ) ;

        InvocationCallback callback = invocation ; 
        if  ( corInv != null )  { 
            callback =  ( )  - > CoroutinesUtils . invokeSuspendingFunction ( method , corInv . getTarget ( ) , corInv . getArguments ( ) ) ; 
        } 
        Object result = txSupport . invokeWithinTransaction ( method ,targetClass , callback , txAttr ,  ( ReactiveTransactionManager ) tm ) ; 
        if  ( corInv != null )  { 
            Publisher < ? > pr =  ( Publisher < ? > ) result ; 
            return  ( hasSuspendingFlowReturnType ? KotlinDelegate . asFlow ( pr )  : 
                    KotlinDelegate .awaitSingleOrNull ( pr , corInv . getContinuation ( ) ) ) ; 
        } 
        return result ; 
    }

    PlatformTransactionManager ptm =  asPlatformTransactionManager ( tm ) ; 
    // Get method ID string 
    final String joinpointIdentification =  methodIdentification ( method , targetClass , txAttr ) ;

    if  ( txAttr == null ||  ! ( ptm instanceof  CallbackPreferringPlatformTransactionManager ) )  { 
        // Standard transaction management: getTransaction/commit/rollback calls.

        // Create transaction information and decide whether to create transaction 
        TransactionInfo txInfo =  createTransactionIfNecessary ( ptm , txAttr , joinpointIdentification ) ;

        Object retVal ; 
        try  { 
            // Execute the original method 
            retVal = invocation . proceedWithInvocation ( ) ; 
        } 
        catch  ( Throwable ex )  { 
            // If an exception occurs, complete the transaction according to the specific exception (rollback will only be performed if the rollback conditions are met) 
            completeTransactionAfterThrowing ( txInfo , ex ) ; 
            throw ex ; 
        } 
        finally  { 
            // clear transaction information 
            cleanupTransactionInfo ( txInfo ) ; 
        }

        if  ( retVal != null && vavrPresent && VavrDelegate . isVavrTry ( retVal ) )  { 
            TransactionStatus status = txInfo . getTransactionStatus ( ) ; 
            if  ( status != null && txAttr != null )  { 
                retVal = VavrDelegate . evaluateTryFailure ( retVal , txAttr , status) ; 
            } 
        }

        // After the original method is successfully executed, the transaction can be submitted 
        commitTransactionAfterReturning ( txInfo ) ; 
        return retVal ; 
    } 
    else  { 
        Object result ; 
        final ThrowableHolder throwableHolder =  new  ThrowableHolder ( ) ;

        try  { 
            // hand over to transaction manager for execution 
            result =  ( ( CallbackPreferringPlatformTransactionManager ) ptm ) . execute ( txAttr , status - >  { 
                // prepare transaction 
                TransactionInfo txInfo =  prepareTransactionInfo ( ptm , txAttr , joinpointIdentification , status ) ; 
                try  { 
                    // execute Original method 
                    Object retVal = invocation .proceedWithInvocation ( ) ; 
                    if  ( retVal != null && vavrPresent && VavrDelegate . isVavrTry ( retVal ) )  { 
                        retVal = VavrDelegate . evaluateTryFailure ( retVal , txAttr , status ) ; 
                    } 
                    return retVal ; 
                } 
                catch  ( Throwable ex )  { 
                    // judgment based on current Whether the exception is rolled back 
                    if  (txAttr . rollbackOn ( ex ) )  { 
                        // RuntimeException can trigger transaction rollback (implemented by DefaultTransactionAttribute) 
                        // Spring framework considers Checked exceptions to be business, coder needs to handle 
                        if  ( ex instanceof  RuntimeException )  { 
                            throw  ( RuntimeException ) ex ; 
                        } 
                        else  { 
                            throw  new  ThrowableHolderException ( ex ) ; 
                        } 
                    } 
                    else  { 
                        // No need to rollback, but because of an exception, you need to return null 
                        throwableHolder . throwable =ex ; 
                        return null ; 
                    } 
                } 
                finally  { 
                    // clear transaction information 
                    cleanupTransactionInfo ( txInfo ) ; 
                } 
            } ) ; 
        } 
        catch  ( ThrowableHolderException ex )  { 
            throw ex . getCause ( ) ; 
        } 
        catch  ( TransactionSystemException ex2 )  { 
            if  ( throwableHolder . throwable ! = null )  {
                logger . error ( "Application exception overridden by commit exception" , throwableHolder . throwable ) ; 
                ex2 . initApplicationException ( throwableHolder . throwable ) ; 
            } 
            throw ex2 ; 
        } 
        catch  ( Throwable ex2 )  { 
            if  ( throwableHolder . throwable != null )  { 
                logger . error ("Application exception overridden by commit exception" , throwableHolder . throwable ) ; 
            } 
            throw ex2 ; 
        }

        if  ( throwableHolder . throwable != null )  { 
            // if there is an exception, 
            throw throwableHolder . throwable ; 
        } 
        return result ; 
    } 
}

protected  void  completeTransactionAfterThrowing ( @Nullable TransactionInfo txInfo , Throwable ex )  { 
    if  ( txInfo != null && txInfo . getTransactionStatus ( )  != null )  { 
        if  ( logger . isTraceEnabled ( ) )  { 
            logger . trace ( "Completing transaction for ["  + txInfo.getJoinpointIdentification ( ) _ _ + 
                    "] after exception: "  + ex ) ; 
        } 
        if  ( txInfo . transactionAttribute != null && txInfo . transactionAttribute . rollbackOn ( ex ) )  { 
            // If the transaction rollback requirements are met, the transaction will be rolled back

            try  { 
                txInfo . getTransactionManager ( ) . rollback ( txInfo . getTransactionStatus ( ) ) ; 
            } 
            catch  ( TransactionSystemException ex2 )  { 
                logger . error ( "Application exception overridden by rollback exception" , ex ) ; 
                ex2 . initApplicationException ( ex ) ; 
                throw ex2 ; 
            }
            catch  ( RuntimeException  | Error ex2 )  { 
                logger . error ( "Application exception overridden by rollback exception" , ex ) ; 
                throw ex2 ; 
            } 
        } 
        else  { 
            // Does not meet the transaction rollback requirements, no need to roll back the transaction

            try  { 
                // commit transaction 
                txInfo . getTransactionManager ( ) . commit ( txInfo . getTransactionStatus ( ) ) ; 
            } 
            catch  ( TransactionSystemException ex2 )  { 
                logger . error ( "Application exception overridden by commit exception" , ex ) ; 
                ex2 . initApplicationException ( ex ) ; 
                throw ex2 ;
            } 
            catch  ( RuntimeException  | Error ex2 )  { 
                logger . error ( "Application exception overridden by commit exception" , ex ) ; 
                throw ex2 ; 
            } 
        } 
    } 
}

protected PlatformTransactionManager determineTransactionManager ( @Nullable TransactionAttribute txAttr )  { 
    if  ( txAttr == null ||  this . beanFactory == null )  { 
        // directly return the global transaction manager 
        return  getTransactionManager ( ) ; 
    }

    // Get the transaction manager name specified by the transaction attribute 
    String qualifier = txAttr . getQualifier ( ) ; 
    if  ( StringUtils . hasText ( qualifier ) )  { 
        // If the transaction manager name is specified, look up the corresponding Bean according to the name 
        return  determineQualifiedTransactionManager ( this . beanFactory , qualifier ) ; 
    } 
    else  if  ( StringUtils . hasText ( this . transactionManagerBeanName ) )  {
        // If the global transaction manager name is specified, look up the corresponding Bean according to the name 
        return  determineQualifiedTransactionManager ( this . beanFactory ,  this . transactionManagerBeanName ) ; 
    } 
    else  { 
        // Get the global transaction manager 
        PlatformTransactionManager defaultTransactionManager =  getTransactionManager ( ) ; 
        if  ( defaultTransactionManager = = null )  { 
            // get the default transaction manager from the local cache 
            defaultTransactionManager =  this . transactionManagerCache . get( DEFAULT_TRANSACTION_MANAGER_KEY ) ; 
            if  ( defaultTransactionManager == null )  { 
                // Get PlatformTransactionManager from container 
                defaultTransactionManager =  this . beanFactory . getBean ( PlatformTransactionManager . class ) ; 
                // Cache the transaction manager 
                this . transactionManagerCache . putIfAbsent ( 
                        DEFAULT_TRANSACTION_MANAGER_KEY , defaultTransactionManager ) ; 
            } 
        }
        return defaultTransactionManager ; 
    } 
}

private PlatformTransactionManager determineQualifiedTransactionManager ( BeanFactory beanFactory , String qualifier )  { 
    // Get the transaction manager with the specified name from the local cache 
    PlatformTransactionManager txManager =  this . transactionManagerCache . get ( qualifier ) ; 
    if  ( txManager == null )  { 
        // The local cache does not exist , the transaction manager is looked up from the container by type and name 
        txManager = BeanFactoryAnnotationUtils . qualifiedBeanOfType ( 
                beanFactory ,PlatformTransactionManager . class , qualifier ) ; 
        // Cache the transaction manager, so that next time you can get 
        this directly from the local cache . transactionManagerCache . putIfAbsent ( qualifier , txManager ) ; 
    } 
    return txManager ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233

BeanFactoryTransactionAttributeSourceAdvisor

existProxyTransactionManagementConfigurationin, forBeanFactoryTransactionAttributeSourceAdvisoralready setupTransactionInterceptorAs an enhancement, setAnnotationTransactionAttributeSourceAs a transaction property source, the code is as follows:

private  final TransactionAttributeSourcePointcut pointcut =  new  TransactionAttributeSourcePointcut ( )  { 
		@Override 
		@Nullable 
		protected TransactionAttributeSource getTransactionAttributeSource ( )  { 
			return transactionAttributeSource ; 
		} 
	} ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

BeanFactoryTransactionAttributeSourceAdvisorInternalPointcutforTransactionAttributeSourcePointcut,useAnnotationTransactionAttributeSourceAs a transaction attribute source, to implement cut-in matching judgment, the code is as follows:

public  boolean  matches ( Method method ,  @Nullable Class < ? > targetClass )  { 
    // TransactionalProxy not supported 
    if  ( targetClass != null && TransactionalProxy . class . isAssignableFrom ( targetClass ) )  { 
        return  false ; 
    } 
    // Get transaction attribute source 
    TransactionAttributeSource tas =  getTransactionAttributeSource ( ) ; 
    // If the current method has a transaction attribute, the match is successful
    return  ( tas == null || tas . getTransactionAttribute ( method , targetClass )  != null ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

TransactionManagementConfigurer

The Spring Boot framework provides by defaultDataSourceTransactionManager,JpaTransactionManagerTransaction manager, the user can implement the interface byTransactionManagementConfigurerto provide customPlatformTransactionManageraccomplish;

source code

public  interface  TransactionManagementConfigurer  { 
    PlatformTransactionManager annotationDrivenTransactionManager ( ) ; 
}
  • 1
  • 2
  • 3

configure

@Configuration 
public  class  JdbcConfig  implements  TransactionManagementConfigurer  {

    @Override 
    public PlatformTransactionManager annotationDrivenTransactionManager ( )  { 
        DataSourceTransactionManager dataSourceTransactionManager =  new  DataSourceTransactionManager ( dataSource ) ; 
        return dataSourceTransactionManager ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Brief analysis

when customTransactionManagementConfigurerOnce implemented and put into the container,AbstractTransactionManagementConfiguration.setConfigurersmethod will search all theTransactionManagementConfigurerImplement (only one is supported) with a custom transaction manager as the global default transaction manager.

@Transactional

source code

@Target ( { ElementType . METHOD , ElementType . TYPE } ) 
@Retention ( RetentionPolicy . RUNTIME ) 
@Inherited 
@Documented 
public @interface Transactional { _  

    // Specify the transaction manager name 
    @AliasFor ( "transactionManager" ) 
    String value ( )  default  "" ;

    // Specify the transaction manager name 
    @AliasFor ( "value" ) 
    String transactionManager ( )  default  "" ;

    // 
    Propagation propagation ( )  default Propagation . REQUIRED ;

    // Transaction isolation level 
    Isolation isolation ( )  default Isolation . DEFAULT ;

    // Transaction timeout 
    int  timeout ( )  default TransactionDefinition . TIMEOUT_DEFAULT ;

    // read only 
    boolean  readOnly ( )  default  false ;

    // Rollback exception type 
    Class < ?  extends  Throwable > [ ]  rollbackFor ( )  default  { } ;

    // Rollback exception class name 
    String [ ]  rollbackForClassName ( )  default  { } ;

    // Do not rollback exception type 
    Class < ?  extends  Throwable > [ ]  noRollbackFor ( )  default  { } ;

    // Do not rollback exception class name 
    String [ ]  noRollbackForClassName ( )  default  { } ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

use

  1. Annotated on a class, indicating that all methods of the class are annotated;
  2. The annotation on the method takes precedence over the annotation on the class where the method is located, if it exists;

Difference from @EnableAsync

  • @EnableTransactionManagementWith automatic proxy creatorAutoProxyCreatorimplement proxy;
  • @EnableAsyncWith Bean Post ProcessorBeanPostProcessorimplement proxy;

Transaction ineffective reason

  1. The method of transaction entry must be a public method by default, seeAnnotationTransactionAttributeSource;
  2. Spring framework only rolls back RuntimeException;
  3. Business and transaction are in the same thread;
  4. If a non-transactional method calls a transactional method in the same class, the transaction does not take effect;
    1. A transaction method calls a transaction/non-transaction method in the same class, and the transaction of the calling method takes effect;
    2. The non-transactional method calls the transactional method through the proxy object of this class, and the transaction of the called method takes effect;

Tags: Spring Boot source code analysis @EnableTransactionManagement

Spring Boot spring boot

Related: Spring Boot source code analysis @EnableTransactionManagement