【Spring】@Transactional解释 (转)

 

Spring是当今广泛使用的框架,为Java核心堆栈带来了许多强大的功能和扩展。然而,大多数人倾向于使用这些功能,而不了解其潜在的机制。

由于现实生活中没有“魔法”,我们将在这一系列文章中深入研究与事务和数据库相关的一些Spring功能。

第一篇文章是处理着名的@Transactional注释,为开发者节省了管理低级别事务代码的负担。

第二篇文章可以在这里:
Spring @ PersistenceContext / @ PersistenceUnit解释

注意:以下代码分析使用Spring 3.0.5正式发布我们仅关注J2SE环境(无EJB)JPA API进行数据库管理。然而,分析的代码是通用的,在一定程度上适用于其他情况(J2EE平台,特定供应商JPA API ...)

我的用法和用例

1
2
3
4
@Transactional(value = "myTransactionManager", propagation = Propagation.REQUIRED)
public void myMethod()
{
 ...
}

@Transactional注释value属性不是必需的。如果没有提到,Spring将默认查看在上下文中声明的名称为“ transactionManager ”(defaultConvention)的bean 

1
2
3
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

II在春天的注册

1
<tx:annotation-driven/>

要使@Transactional注解工作,您应该声明<tx:annotation-driven>标签(tx是“ http://www.springframework.org/schema/tx  的命名空间的快捷方式

III代码分析

豆注册

 
在本章中,我们将看到如何在Spring上下文中处理<tx:annotation-driven>标签声明

1)org.springframework.transaction.config。AnnotationDrivenBeanDefinitionParser
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Parses the '<code></code>' tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
* with the container as necessary.
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
  String mode = element.getAttribute("mode");
  if ("aspectj".equals(mode)) {
       // mode="aspectj"
      registerTransactionAspect(element, parserContext);
  }
  else {
      // mode="proxy"
      // DEFAULT MODE
      AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
  }
  return null;
}

对于大多数用户,我们属于else块(mode =“proxy”),所以我们调用AopAutoProxyConfigurer .configureAutoProxyCreator()

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
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
private static class AopAutoProxyConfigurer {
 
  public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
 
     if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
       Object eleSource = parserContext.extractSource(element);
 
       // Create the TransactionAttributeSource definition.
       RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
       sourceDef.setSource(eleSource);
       sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
 
       // The bean AnnotationTransactionAttributeSource is created and registed dynamically here
       String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
 
       // Create the TransactionInterceptor definition.
       // Point A
       RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
       interceptorDef.setSource(eleSource);
       interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
 
       // Set the declared transaction manager in   to the transactionInterceptor, when available
       registerTransactionManager(element, interceptorDef);
       interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
 
       //  The bean TransactionInterceptor is created and registed dynamically here
       String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
 
       // Create the TransactionAttributeSourceAdvisor definition.
 
       // This bean is an AOP definition
       RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
       advisorDef.setSource(eleSource);
       advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
 
       // Inject the bean AnnotationTransactionAttributeSource into the AOP definition
       // Point B
       advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
 
       // Definition of advice bean = TransactionInterceptor previously declared
       advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
       if (element.hasAttribute("order")) {
          advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
       }
       //  The bean BeanFactoryTransactionAttributeSourceAdvisor is created and registed dynamically here
       parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
 
       CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
       compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
       compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
       compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
       parserContext.registerComponent(compositeDef);
    }
  }
 
  // Retrieve the transactionManager attribute defined on  when available
  // Example
  private static void registerTransactionManager(Element element, BeanDefinition def) {
        def.getPropertyValues().add("transactionManagerBeanName",
                TxNamespaceHandler.getTransactionManagerName(element));
  }
}

事务拦截器类是在第19行创建的

然后在<tx:annotation-driven>中声明的事务管理器在Spring上下文中被搜索并附加到该事务拦截器第24行

最后,bean在第28行的Spring环境中注册

从  第32行第47行,Spring声明一个  TransactionAttributeSourceAdvisor bean并将其注册到上下文中。
 

B @Transactional解析

 

1)org.springframework.transaction.interceptor。BeanFactoryTransactionAttributeSourceAdvisor扩展了AbstractBeanFactoryPointcutAdvisor

 
在本章中,我们将了解如何在运行时通过Spring解析@Transactional注释以检索与事务相关的属性

1
2
3
4
6
7
8
9
10
11
12
13
// Injected at Point B
private TransactionAttributeSource transactionAttributeSource;
 
// Define a pointcut here for AOP, injecting the transactionAttributeSource that was
// set in AopAutoProxyConfigurer
// see Point B
private final TransactionAttributeSourcePointcut pointcut =
   new TransactionAttributeSourcePointcut() {
      @Override
      protected TransactionAttributeSource getTransactionAttributeSource() {
        return transactionAttributeSource;
      }
   };

这里Spring刚刚定义了一个poincut顾问。它由一个由TransactionAttributeSourcePointcut类处理的poincut组成这里的transactionAttributeSource是在overriden getTransactionAttributeSource()方法中动态传递给匿名类的

2)抽象类org.springframework.transaction.interceptor。TransactionAttributeSourcePointcut
1
2
3
4
6
7
8
9
10
11
12
13
abstract class org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut
 
  // Determine whether a call on a particular method matches the poincut
  // If it matches then the advice bean will be called
  // The advice bean that has been registered for this pointcut is the
  // TransactionInterceptor class (see Point A)
  public boolean matches(Method method, Class targetClass) {
  TransactionAttributeSource tas = getTransactionAttributeSource();
 
  // Call getTransactionAttribute of the injected transactionAttributeSoirce
  // (see Point C)
  return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

这个抽象类只定义maches()方法的默认行为,以检查连接点是否与此poincut匹配

3)org.springframework.transaction.annotation。AnnotationTransactionAttributeSource扩展AbstractFallbackTransactionAttributeSource
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Should be false in a context of J2SE
private static final boolean ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute",
                                  AnnotationTransactionAttributeSource.class.getClassLoader());
 
// Default constructor
public AnnotationTransactionAttributeSource() {
  this(true);
}
 
// publicMethodsOnly = true because this bean has been registered dynamically
// by AopAutoProxyConfigurer with no argument so the default constructor above applies
//
// ejb3Present = false
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
      this.publicMethodsOnly = publicMethodsOnly;
      this.annotationParsers = new LinkedHashSet(2);
      this.annotationParsers.add(new SpringTransactionAnnotationParser());
      if (ejb3Present) {
         this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
      }
  }

AnnotationTransactionAttributeSource的第二个构造函数SpringTransactionAnnotationParser注册@Transactional注释的默认解析器

4)org.springframework.transaction.interceptor。AbstractFallbackTransactionAttributeSource

C点D点

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
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
// Point C
public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
      // First, see if we have a cached value.
      Object cacheKey = getCacheKey(method, targetClass);
      Object cached = this.attributeCache.get(cacheKey);
      if (cached != null) {
        ....
        ....
        // Not interesting code
      }
      else {
        // We need to work it out.
        // (see Point D below)
        TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
        // Put it in the cache.
        if (txAtt == null) {
          this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
        }
        else {
          if (logger.isDebugEnabled()) {
             logger.debug("Adding transactional method '" + method.getName()
             + "' with attribute: " + txAtt);
          }
          this.attributeCache.put(cacheKey, txAtt);
        }
        return txAtt;
      }
  }
 
// Point D
private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {
     // Don't allow no-public methods as required.
     // Here allowPublicMethodsOnly() will return true because we set the attribute
     // publicMethodOnly = true in the constructor of AnnotationTransactionAttribute
     if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
     }
 
     // Ignore CGLIB subclasses - introspect the actual user class.
     Class userClass = ClassUtils.getUserClass(targetClass);
 
     // The method may be on an interface, but we need attributes from the target class.
     // If the target class is null, the method will be unchanged.
     Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
 
     // If we are dealing with method with generic parameters, find the original method.
     specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
 
     // Find the @Transactional attributes of the method in the target class.
     // (see Point E)
     TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
     if (txAtt != null) {
        return txAtt;
     }
     ...
     // Not interesting code
 }

抽象类getTransactionAttribute()最终将工作委派给computeTransactionAttribute()方法。

首先它确定目标类(如果注释被放在接口方法上),然后调用方法findTransactionAttribute()

5)org.springframework.transaction.annotation。AnnotationTransactionAttributeSource扩展AbstractFallbackTransactionAttributeSource

点E

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
// Point E
protected TransactionAttribute findTransactionAttribute(Method method) {
      // See below
      return determineTransactionAttribute(method);
}
 
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
      for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
          // (see Point F)
          TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
          if (attr != null) {
              return attr;
          }
      }
      return null;
}

再次,真正的工作不是在这里完成的,而是委派给注释解析器类SpringTransactionAnnotationParser之前注册在AnnotationTransactionAttributeSource的构造函数

6)org.springframework.transaction.annotation。SpringTransactionAnnotationParser

F点

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
// Point F
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
      Transactional ann = ae.getAnnotation(Transactional.class);
      if (ann == null) {
          for (Annotation metaAnn : ae.getAnnotations()) {
              // @Transactional annotation
              ann = metaAnn.annotationType().getAnnotation(Transactional.class);
              if (ann != null) {
                  break;
              }
          }
      }
      if (ann != null) {
          //See below
          return parseTransactionAnnotation(ann);
      }
      else {
          return null;
      }
  }
 
  public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
      RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
      rbta.setPropagationBehavior(ann.propagation().value());
      rbta.setIsolationLevel(ann.isolation().value());
      rbta.setTimeout(ann.timeout());
      rbta.setReadOnly(ann.readOnly());
 
      /* Set qualifier name in the case multiple transaction managers are used
       * bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
       *    property name="entityManagerFactory" ref="myEntityManagerFactory" /
       * /bean
       *
       * @Transactional(value = "myTransactionManager")
       */
      rbta.setQualifier(ann.value());
      ArrayList rollBackRules = new ArrayList();
      ...
      // Not interesting code
      return rbta;
  }

解析器将检索@Transactional注释的所有属性,其中:

  • 传播行为
  • 隔离级别
  • 事务的超时值
  • readOnly标志
  • 和所有的最重要的属性:value,它对应于在Spring上下文中声明的负责当前事务的transactionManager的bean名称。

如果省略,则value属性默认为“transactionManager”在处理多个数据库或多个数据源应用程序时,在Spring上下文中定义了多个transactionManager,所以该对于帮助Spring选择正确的一个是重要的。
 

C事务拦截器调用

 

在本章中,我们将介绍Spring如何实现事务划分

1)org.springframework.transaction.interceptor。TransactionInterceptor扩展了TransactionAspectSupport
1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// The real job is done here
public Object invoke(final MethodInvocation invocation) throws Throwable {
 
      // Work out the target class: may be <code>null</code>.
      // The TransactionAttributeSource should be passed the target class
      // as well as the method, which may be from an interface.
      Class<?> targetClass = (invocation.getThis() != null ?
                            AopUtils.getTargetClass(invocation.getThis()) : null);
 
      // If the transaction attribute is null, the method is non-transactional.
      //Similar to Point C
      final TransactionAttribute txAttr =
              getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(),
                                                                        targetClass);
 
       // (see Point G)
      final PlatformTransactionManager tm = determineTransactionManager(txAttr);
      final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);
 
      // The txAttr is not null but the transactionManager is NOT an instance
      // of CallbackPreferringPlatformTransactionManager so we still enter the if block
      if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
 
          // Standard transaction demarcation with getTransaction() and commit/rollback calls.
          // (see Point H)
          TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
 
           
          Object retVal = null;
          try {
              // This is an around advice: Invoke the next interceptor in the chain.
              // This will normally result in a target object being invoked.
              retVal = invocation.proceed();
          }
          catch (Throwable ex) {
              // target invocation exception
              completeTransactionAfterThrowing(txInfo, ex);
              throw ex;
          }
          finally {
              cleanupTransactionInfo(txInfo);
          }
          // Commit after the method call returns
          // (see Point O)
          commitTransactionAfterReturning(txInfo);
          return retVal;
      }
  • First Spring检索事务属性(第12,13和14行
  • 然后它从Spring上下文和事务属性(第17行获取事务管理器
  • 交易由基础实体经理创建(第26行
  • 调用目标方法(第33行
  • 从方法调用返回后,事务被提交(第45行
2)public abstract class org.springframework.transaction.interceptor。TransactionAspectSupport

G点H点

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
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
//Point G
protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
      if (this.transactionManager != null || this.beanFactory == null || txAttr == null) {
          return this.transactionManager;
      }
      String qualifier = txAttr.getQualifier();
     
      // Case when the transaction manager has been declared directly in the @Transactional annotation
      //  Example @Transactional(value = "myTransactionManager")
      if (StringUtils.hasLength(qualifier)) {
          return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier);
      }
 
      // Case when the transaction manager has been declared in the tx:annotation-driven tag
      //  Example tx:annotation driven transaction-manager="myTransactionManager"
      else if (this.transactionManagerBeanName != null) {
          return this.beanFactory.getBean(this.transactionManagerBeanName,
                                           PlatformTransactionManager.class);
      }
     ...
     // Not interesting code
      
  }
 
// Point H
protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm,
                                                      TransactionAttribute txAttr,
                                                      final String joinpointIdentification)
{
      // If no name specified, apply method identification as transaction name.
      // This is the default case
      if (txAttr != null && txAttr.getName() == null) {
          txAttr = new DelegatingTransactionAttribute(txAttr) {
              @Override
              public String getName() {
                  return joinpointIdentification;
              }
          };
      }
 
      TransactionStatus status = null;
      if (txAttr != null) {
          if (tm != null) {
              // Call to the AbstractPlatFormTransactionManager to start a transaction
              // (see Point I)
              status = tm.getTransaction(txAttr);
          }
          else {
              if (logger.isDebugEnabled()) {
                  logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                          "] because no transaction manager has been configured");
              }
          }
      }
      return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
  }

这个课程正在做两个主要任务:

  • 确定事务管理器来管理当前事务,使用@Transactional注释value属性或者使用tx:annotation-driven标签transaction-manager属性
  • 将事务的创建委托给AbstractPlatFormTransactionManager
3)抽象类org.springframework.transaction.support。AbstractPlatformTransactionManager

点我

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
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
//Point I
public final TransactionStatus getTransaction(TransactionDefinition definition) throws
TransactionException {
      // Retrieve the transaction from JpaTransactionManager.doGetTransaction()
      // (see Point J)
      Object transaction = doGetTransaction();
 
      // Cache debug flag to avoid repeated checks.
      boolean debugEnabled = logger.isDebugEnabled();
 
      if (definition == null) {
          // Use defaults if no transaction definition given.
          definition = new DefaultTransactionDefinition();
      }
 
      if (isExistingTransaction(transaction)) {
          // Existing transaction found -> check propagation behavior to find out how to behave.
          return handleExistingTransaction(definition, transaction, debugEnabled);
      }
 
      ...
 
      // No existing transaction found -> check propagation behavior to find out how to proceed.
      if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
          throw new IllegalTransactionStateException(
                  "No existing transaction found for transaction marked with propagation 'mandatory'");
      }
      // Our case
      else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
              definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
          definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
          SuspendedResourcesHolder suspendedResources = suspend(null);
          if (debugEnabled) {
              logger.debug("Creating new transaction with name [" + definition.getName()
                           + "]: " + definition);
          }
          try {
              boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
              /*
              * Return a new DefaultTransactionStatus(transaction,            
              *                                       newTransaction = true,
              *                                       newSynchronization = true,
              *                                       definition.isReadOnly(),debugEnabled,
              *                                       suspendedResources)
              *  for a new transaction
              */
              DefaultTransactionStatus status = newTransactionStatus(
                      definition, transaction, true, newSynchronization,
                      debugEnabled, suspendedResources);
              
              // Real job here, delegates call to JpaTransactionManager.doBegin()
              // (see Point K)
              doBegin(transaction, definition);
              
              // Set some synchronization flags to the TransactionSynchronizationManager thread local
              prepareSynchronization(status, definition);
              return status;
          }
          catch (RuntimeException ex) {
              resume(null, suspendedResources);
              throw ex;
          }
          catch (Error err) {
              resume(null, suspendedResources);
              throw err;
          }
      }
      else {
          // TransactionDefinition = PROPAGATION_SUPPORTS or PROPAGATION_NOT_SUPPORTED
          //                         or PROPAGATION_NEVER
          // Create "empty" transaction: no actual transaction, but potentially synchronization.
          boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
          return prepareTransactionStatus(definition, null, true, newSynchronization,
                                          debugEnabled, null);
      }
  }

getTransaction()委托创作和交易本身的底层开始JpaTransactionManager接口

我们可以看到Spring如何管理不同类型的传播行为。

4)org.springframework.orm.jpa。JpaTransactionManager接口

J&Point K

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
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
//Point J
protected Object doGetTransaction() {
      JpaTransactionObject txObject = new JpaTransactionObject();
      txObject.setSavepointAllowed(isNestedTransactionAllowed());
 
      // Try to retrieve an EntityManagerHolder from the thread local map of
      // TransactionSynchronizationManager using the EntityManagerFactory as search key
      // The EntityManagerFactory was injected in the JpaTransactionManager in the XML config file
      //
      // bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
      //     property name="entityManagerFactory" ref="myEntityManagerFactory"
      // bean
      //
      //
      
      // this EntityManagerHolder might be null when called the first time
      EntityManagerHolder emHolder = (EntityManagerHolder)
              TransactionSynchronizationManager.getResource(getEntityManagerFactory());
      if (emHolder != null) {
          if (logger.isDebugEnabled()) {
              logger.debug("Found thread-bound EntityManager [" +
                      emHolder.getEntityManager() + "] for JPA transaction");
          }
          // attach the EntityManagerHolder to the JpaTransactionObject
          // the flag false is set to the property newEntityManagerHolder
          txObject.setEntityManagerHolder(emHolder, false);
      }
 
      // The datasource is injected directly into the JpaTransactionManager
      // after bean initialization (afterPropertySet())
      // by inspecting the injected EntityManagerFactory
      //
      //  bean id="myEntityManagerFactory"
      //       class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
      //       property name="dataSource" ref="myDataSource"
      //       property name="persistenceUnitName" value="myPersistenceUnit"
      //  bean
      //
      //
 
      // this test always evaluates to true
      if (getDataSource() != null) {
          ConnectionHolder conHolder = (ConnectionHolder)
                  TransactionSynchronizationManager.getResource(getDataSource());
           // attach a connectionHolder to the JpaTransactionObject (to start JDBC transaction probably)
          txObject.setConnectionHolder(conHolder);
      }
      return txObject;
  }
 
//Point K
protected void doBegin(Object transaction, TransactionDefinition definition) {
      JpaTransactionObject txObject = (JpaTransactionObject) transaction;
 
      ...
 
      try {
          
          // The EntityManagerHolder can be null if not registered already in the
          // thread local map of TransactionSynchronizationManager
          if (txObject.getEntityManagerHolder() == null ||
                  txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {
              // Create a new EntityManager from the EntityManagerFactory
              EntityManager newEm = createEntityManagerForTransaction();
              if (logger.isDebugEnabled()) {
                  logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction");
              }
 
              // attach the EntityManagerHolder to the JpaTransactionObject
              // newEntityManagerHolder = true
              // because the EntityManager has just been created from scratch
              txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);
          }
 
          EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
          final int timeoutToUse = determineTimeout(definition);
 
         /* Delegate to JpaDialect for actual transaction begin, passing the EntityManager
          
          *   META-INF|persistence.xml
          *
          *   persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL"
          *     provider
          *         org.hibernate.ejb.HibernatePersistence
          *     provider
          *     properties
          *         property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"
          *     ...
          */
 
          // (see Point L for HibernateJpaDialect)
          Object transactionData = getJpaDialect().beginTransaction(em,
                  new DelegatingTransactionDefinition(definition) {
                      @Override
                      public int getTimeout() {
                          return timeoutToUse;
                      }
                  });
 
          // Set transaction data to the JpaTransactionObject
          txObject.setTransactionData(transactionData);
 
          // Register transaction timeout.
          if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
              txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse);
          }
 
          // Register the JPA EntityManager's JDBC Connection for the DataSource, if set.
          if (getDataSource() != null) {
              // Retrieve the underlying JDBC connection by calling the JPA Dialect class  
              ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());
              if (conHandle != null) {
                  ConnectionHolder conHolder = new ConnectionHolder(conHandle);
                  if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
                      conHolder.setTimeoutInSeconds(timeoutToUse);
                  }
                  if (logger.isDebugEnabled()) {
                      logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
                  }
 
                  // Set the JDBC connection to the current Threadlocal resources map, the
                  // datasource being the key                   
                  TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
                  
                  // Set JDBC connection holder to the JpaTransactionObject
                  txObject.setConnectionHolder(conHolder);
              }
              else {
                  if (logger.isDebugEnabled()) {
                      logger.debug("Not exposing JPA transaction [" + em
                                   + "] as JDBC transaction because JpaDialect [" +
                              getJpaDialect() + "] does not support JDBC Connection retrieval");
                  }
              }
          }
 
          // If the EntityManager has been created from scratch (see Point L)
          if (txObject.isNewEntityManagerHolder()) {
 
               // register the EntityManagerHolder to the current Threadlocal resources map, the EntityManagerFactory being the key
              TransactionSynchronizationManager.bindResource(
                      getEntityManagerFactory(), txObject.getEntityManagerHolder());
          }
          txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);
      }
 
      catch (TransactionException ex) {
          closeEntityManagerAfterFailedBegin(txObject);
          throw ex;
      }
      catch (Exception ex) {
          closeEntityManagerAfterFailedBegin(txObject);
          throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex);
      }
  }

大部分重要工作都是在这个班上完成的。

点J:doGetTransaction()

  • 第一个Spring尝试查看TransactionSynchronizationManager ThreadLocal地图,看看是否有一个现有的实体管理器使用实体管理器工厂作为搜索关键字(第17和18行

     

    • 实体管理器工厂在Spring XML定义中注入到Jpa事务管理器中。
      如果没有明确地做到这一点,Spring将在事务管理器初始化期间通过在上下文中查找名为“entityManagerFactory”(默认名称)的bean来完成此工作

       

      • 如果在ThreadLocal地图中找到一个实体管理器,那么Spring会将它包裹在一个EntityManagerHolder对象上,该对象的布尔标志为isNew = false,因为这个实体管理器是在手中的某个地方创建的。第26行
      • 否则,EntityManagerHolder中的JpaTransactionObject
    • Spring还检索为此事务管理器声明dataSource,并将其存储在JpaTransactionObject第46行

点K:doBegin()

  • Spring检查JpaTransactionObject以查找EntityManagerHolder
    • 如果没有找到,Spring将实体管理器的创建委托给附属实体管理器工厂(第65行)。然后,Spring 将该EntityManagerHolder对象围绕该实体管理器标记为isNew = true,表示该实体管理器是在当前事务中创建的,而不是之前(第73行
  • 然后,Spring将创建一个新的JDBC事务委托给基础的JPA方言(第91行到第97行)。该方言在META-INF / persistence.xml文件中为每个persistenceUnit定义
  • Spring将当前JDBC连接注册到TransactionSynchronizationManager ThreadLocal映射,使用dataSource作为键(第125行
  • 如果在JpaTransactionObject上设置了标志isNew = true,那么Spring也将使用实体管理器工厂作为关键字,将新创建的实体管理器注册到TransactionSynchronizationManager ThreadLocal映射。
5)org.springframework.orm.jpa。HibernateJpaDialect扩展了DefaultJpaDialect

点L

1
2
3
4
6
7
8
9
10
11
12
13
  // Point L
  public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
            throws PersistenceException, SQLException, TransactionException {
 
    if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
        getSession(entityManager).getTransaction().setTimeout(definition.getTimeout());
    }
        // (see Point M)
    super.beginTransaction(entityManager, definition);
 
        // (see Point N)
    return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());
}  

我们来看看HibernateJpaDialect是默认的Jpa方言。我们可以看到,这个类正在调用超类DefaultJpaDialect来启动事务(第9行

然后调用内部方法prepareTransaction() 第12行

6)org.springframework.orm.jpa。DefaultJpaDialect

点M

1
2
3
4
6
7
8
9
10
11
12
  // Point M
  public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
        throws PersistenceException, SQLException, TransactionException {
     
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
        throw new InvalidIsolationLevelException(
                "Standard JPA does not support custom isolation levels - "
                +"use a special JpaDialect for your JPA implementation");
    }
    entityManager.getTransaction().begin();
    return null;
}

交易由实体经理开始。我们可以清楚地看到,只有默认的ISOLATION级别才被vanilla HibernateJpaDialect支持任何尝试将隔离级别设置为其他ISOLATION_DEFAULT将触发异常的其他级别。

7)org.springframework.orm.jpa。HibernateJpaDialect扩展了DefaultJpaDialect

N点

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Point N
public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name) 
throws PersistenceException {
 
  Session session = getSession(entityManager);
  FlushMode flushMode = session.getFlushMode();
  FlushMode previousFlushMode = null;
  if (readOnly) {
      // We should suppress flushing for a read-only transaction.
      session.setFlushMode(FlushMode.MANUAL);
      previousFlushMode = flushMode;
  }
  else {
      // We need AUTO or COMMIT for a non-read-only transaction.
      if (flushMode.lessThan(FlushMode.COMMIT)) {
          session.setFlushMode(FlushMode.AUTO);
          previousFlushMode = flushMode;
      }
  }
  return new SessionTransactionData(session, previousFlushMode);
}

prepareTransaction()方法设置和保存以前的刷新模式,什么更多的,是...

8)org.springframework.transaction.interceptor。TransactionAspectSupport

点O

1
2
3
4
6
7
8
9
10
11
// Point O
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        // Delegate the commit call to the underlying TransactioManager
        // (see Point P)
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

该方法只是将事务提交委托给事务管理器

9)org.springframework.transaction.support。AbstractPlatformTransactionManager

点P

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
// Point P
public final void commit(TransactionStatus status) throws TransactionException {
      if (status.isCompleted()) {
          throw new IllegalTransactionStateException(Transaction is already completed - do not call commit or rollback more than once per transaction");
      }
      ...       
      // Not interesting code
      processCommit(defStatus); // See below
  }
 
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
      try {
          boolean beforeCompletionInvoked = false;
          try {
              // Commit pre-processing, not always implemented by the actual TransactionManager
              prepareForCommit(status);
              triggerBeforeCommit(status);
              triggerBeforeCompletion(status);
                
                  // Delegate the real commit to the actual TransactionManager
                  // (see Point Q)
                  doCommit(status);
              }
            ...
           try {
              // Commit post-processing, not always implemented by the actual TransactionManager
              triggerAfterCommit(status);
            }
          finally {
              triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
          }
      }
  finally {
        // (see Point M)
    cleanupAfterCompletion(status);
  }
      ...
  }

再次,除了调用一些触发代码来准备提交之外,提交的真正工作被委托给方法doCommit()
提交完成之后,调用cleanupAfterCompletion()在必要时清除TransactionSynchronizationManager ThreadLocal映射

10)org.springframework.orm.jpa。JpaTransactionManager接口

点Q

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//Point Q
protected void doCommit(DefaultTransactionStatus status) {
      JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
      if (status.isDebug()) {
          logger.debug("Committing JPA transaction on EntityManager [" +
                  txObject.getEntityManagerHolder().getEntityManager() + "]");
      }
      try {
          // The real commit is done here !
          EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
          tx.commit();
      }
      catch (RollbackException ex) {
          if (ex.getCause() instanceof RuntimeException) {
              DataAccessException dex = getJpaDialect().
                      translateExceptionIfPossible((RuntimeException) ex.getCause());
              if (dex != null) {
                  throw dex;
              }
          }
          throw new TransactionSystemException("Could not commit JPA transaction", ex);
      }
      catch (RuntimeException ex) {
          // Assumably failed to flush changes to database.
          throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());
      }
  }

再次,提交是对getEntityManager()。getTransaction()。commit()的简单调用,没有任何魔术。

11)org.springframework.transaction.support。AbstractPlatformTransactionManager

点M

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
// Point M
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
  status.setCompleted();
  if (status.isNewSynchronization()) {
      TransactionSynchronizationManager.clear();
  }
  if (status.isNewTransaction()) {              
              // (see Point N)
      doCleanupAfterCompletion(status.getTransaction());
  }
  if (status.getSuspendedResources() != null) {
      if (status.isDebug()) {
       logger.debug("Resuming suspended transaction after completion of inner transaction");
      }
      resume(status.getTransaction(), (SuspendedResourcesHolder)
                    status.getSuspendedResources());
  }
}

只是间接的代码,真正的工作是在doCleanupAfterCompletion()

12)org.springframework.orm.jpa。JpaTransactionManager接口

N点

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
  // Point N
  protected void doCleanupAfterCompletion(Object transaction) {
    JpaTransactionObject txObject = (JpaTransactionObject) transaction;
 
    // Remove the entity manager holder from the thread.
    if (txObject.isNewEntityManagerHolder()) {
        TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
    }
    txObject.getEntityManagerHolder().clear();
 
    // Remove the JDBC connection holder from the thread, if exposed.
    if (txObject.hasConnectionHolder()) {
        TransactionSynchronizationManager.unbindResource(getDataSource());
        try {
            getJpaDialect().releaseJdbcConnection(
                                        txObject.getConnectionHolder().getConnectionHandle(),
                    txObject.getEntityManagerHolder().getEntityManager());
        }
        catch (Exception ex) {
            // Just log it, to keep a transaction-related exception.
            logger.error("Could not close JDBC connection after transaction", ex);
        }
    }
    getJpaDialect().cleanupTransaction(txObject.getTransactionData());
 
    // Remove the entity manager holder from the thread.
    if (txObject.isNewEntityManagerHolder()) {
        EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
        if (logger.isDebugEnabled()) {
            logger.debug("Closing JPA EntityManager [" + em + "] after transaction");
        }
        EntityManagerFactoryUtils.closeEntityManager(em);
    }
    else {
        logger.debug("Not closing pre-bound JPA EntityManager after transaction");
    }
}

这里有很多有趣的代码片段:

    • 第6行和第7行:如果JpaTransactionObject的标志为isNew = true,那么Spring将从TransactionSynchronizationManager ThreadLocal地图中删除它实际上isNew = true意味着实体管理器是从头开始为当前事务创建的,现在自从事务被提交以来,没有理由将它保留在ThreadLocal地图

 

    • 类似地,在第32行如果标志isNew = true, Spring将正常关闭实体管理器

 

  • 如果标志isNew = false,则表示当前事务中使用的实体管理器已经在TransactionSynchronizationManagerThreadLocal地图中注册,没有任何反应。它没有被关闭,并且仍然存在于ThreadLocal地图(第35行)中。

 

四,总结

在挖掘@Transactional的Spring代码之后,我们可以说:

    • 代码中有很多间接的间接级别。像commit这样的一个任务需要3个方法调用。我认为这是由于Spring的开放灵活的架构,允许最终用户插入每个组件的自定义实现。还可以通过事务代码尽可能通用来解释这一点,因此它不仅可以应用于JDBC事务,还可以应用于其他类型的事务(JMS,Web Services ...)

 

    • TransactionSynchronizationManager发挥在交易管理中的关键作用。它作为一个线程级的缓存来携带当前的实体管理器沿着当前事务的所有层

 

    • TransactionSynchronizationManager公共方法可以表明,它可以通过编程来获得对实体管理器的生命周期更精细的控制。

 
@Transactional管理的 伪代码

    • 如果TransactionSynchronizationManager.getResource(emf)存在,请使用它

 

    • 否则,检索EntityManagerFactory从头创建一个新的EntityManager实例,并使用TransactionSynchronizationManager.bindResource(emf,em)将其注册到ThreadLocal地图,

 

    • 通过调用基础的JPADialect实现getJpaDialect()来启动一个新的DB事务beginTransaction(...)

 

    • 通过调用entityManager.getTransaction()来提交事务commit()

 

    • 如果当前实体管理器是从头创建的,请从ThreadLocal地图中删除它,并将其关闭

 

  • 否则什么都不做

posted on 2017-08-31 00:18  0X000  阅读(1159)  评论(0编辑  收藏  举报

导航