Spring Boot source code analysis @EnableTransactionManagement
Related Reading
- Spring Boot source code analysis transaction management
- Spring Boot source code analysis @EnableAspectJAutoProxy
- Spring Boot source code analysis @EnableAsync
- Spring Boot source code analysis @EnableCaching
TransactionAutoConfiguration
Spring framework provides @Transactional
annotations to support annotation-driven transaction management;
Spring Boot framework enables annotation-driven transaction management by default, that is, throughTransactionAutoConfiguration
To 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 toTransactionAutoConfiguration
The configuration information can be seen, when the introductionPlatformTransactionManager
(provided by spring-tx), the configuration class takes effect automatically;
DataSourceTransactionManagerAutoConfiguration
will createTransactionManager
, if currently existsAbstractTransactionManagementConfiguration
,Right nowProxyTransactionManagementConfiguration
, then it means that @ has been introducedEnableTransactionManagement
annotation(ProxyTransactionManagementConfiguration
will only be @EnableTransactionManagement
annotation introduction), soEnableTransactionManagementConfiguration
Does not need to take effect; otherwise it will take effect, thereby introducing @EnableTransactionManagement
annotation;
EnableTransactionManagementConfiguration
effective unless specifiedspring.aop.proxy-target-class
The configuration item is false, at this timeJdkDynamicAutoProxyConfiguration
will take effect, otherwiseCglibAutoProxyConfiguration
Take effect, both configurations will introduce @EnableTransactionManagement
Annotation, just annotatedproxyTargetClass
If 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
TransactionManagementConfigurationSelector
inherited fromAdviceModeImportSelector
,AdviceModeImportSelector
Implementation supportAdviceMode
Algorithm template, and supports generic @EnableXxx
Annotation 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
TransactionManagementConfigurationSelector
Implemented 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 importAutoProxyRegistrar
andProxyTransactionManagementConfiguration
;
AutoProxyRegistrar
AutoProxyRegistrar
AchievedImportBeanDefinitionRegistrar
, 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
ProxyTransactionManagementConfiguration
is a configuration class that inherits fromAbstractTransactionManagementConfiguration
;
AbstractTransactionManagementConfiguration
AchievedImportAware
interface, will inject @EnableTransactionManagement
Annotate attribute data and inject at the same timeTransactionManagementConfigurer
Configuration, 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
ProxyTransactionManagementConfiguration
will 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
AnnotationTransactionAttributeSource
Is an annotation-based transaction attribute source, internally maintainedTransactionAnnotationParser
Collection, 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
SpringTransactionAnnotationParser
AchievedTransactionAnnotationParser
Interface, responsible for parsing @Transactional
Note, 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
TransactionInterceptor
inherited 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
existProxyTransactionManagementConfiguration
in, forBeanFactoryTransactionAttributeSourceAdvisor
already setupTransactionInterceptor
As an enhancement, setAnnotationTransactionAttributeSource
As 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
BeanFactoryTransactionAttributeSourceAdvisor
InternalPointcut
forTransactionAttributeSourcePointcut
,useAnnotationTransactionAttributeSource
As 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
,JpaTransactionManager
Transaction manager, the user can implement the interface byTransactionManagementConfigurer
to provide customPlatformTransactionManager
accomplish;
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 customTransactionManagementConfigurer
Once implemented and put into the container,AbstractTransactionManagementConfiguration.setConfigurers
method will search all theTransactionManagementConfigurer
Implement (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
- Annotated on a class, indicating that all methods of the class are annotated;
- The annotation on the method takes precedence over the annotation on the class where the method is located, if it exists;
Difference from @EnableAsync
- @
EnableTransactionManagement
With automatic proxy creatorAutoProxyCreator
implement proxy; - @
EnableAsync
With Bean Post ProcessorBeanPostProcessor
implement proxy;
Transaction ineffective reason
- The method of transaction entry must be a public method by default, see
AnnotationTransactionAttributeSource
; - Spring framework only rolls back RuntimeException;
- Business and transaction are in the same thread;
- If a non-transactional method calls a transactional method in the same class, the transaction does not take effect;
- A transaction method calls a transaction/non-transaction method in the same class, and the transaction of the calling method takes effect;
- The non-transactional method calls the transactional method through the proxy object of this class, and the transaction of the called method takes effect;