Spring 3的Transaction框架背后的设计原则

时间:2012-08-03 05:37:56

标签: spring transactions aspectj

我已经通过spring和AspectJ了解了关于tx的帖子数量。以下是摘要, 说,我有一个服务类及其接口

interface TestService {
 void methodA();
 void methodB();
}

class TestServiceImpl implements TesService {

   @Transactional
   void methodA() {
      methodB();
   }

   @Transactional(propagation=Propagation.NEVER)
   void methodB(){}
}

我的配置

<tx:annotation-driven transaction-manager="jpaTxManager"/>

<bean id="jpaTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"><ref bean="entityManagerFactory"/></property>
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>

<bean id="testService" class="com.motherframework.plugin.test.service.TestServiceImpl">
     <property name="testDAO" ref="testDAO"/>
</bean>

我从某个客户端类调用testService.methodA()。根据Spring的JDK动态代理使用情况,它只关注@Transactional上的methodA(),而@Transactional(propagation=Propagation.NEVER)上只关注methodB()。所以代码执行正确的事务和提交。如果我们使用AspectJ模式,那么它还会在@Transactional(propagation=Propagation.NEVER)上检查methodB()并抛出异常。

现在我的问题是,为什么这个限制是由Spring强加的?现在,Spring设计有两种可能性,

  1. 使用spring是技术限制,他们无法检查methodB()中的注释,尽管它是公开的?但是如果AspectJ可以检查它,那么为什么不是Spring?

  2. 他们故意限制此内部方法调用的AOP检查。这种方法调用(其中目标方法使用不同的transactionPropagation注释)是否违反了正确的设计方法?

3 个答案:

答案 0 :(得分:3)

是的,这是一个技术限制。当您不使用AspectJ时,通过在实际的bean类实例周围返回代理并将此代理返回/注入其他bean来实现事务方面。因此,当您致电testService.methodA()时,会发生以下情况(基本上):

caller ---> transactionalProxy.methodA() ---> testServiceImpl.methodA()

代理在testServiceImpl.methodA()的调用周围应用事务方面:它在之前启动事务,并在之后提交/回滚。

如果您从this.methodB()致电methodA(),则会发生以下情况:

caller ---> transactionalProxy.methodA() ---> testServiceImpl.methodA() ---> testServiceImpl.methodB()

由于您绕过了代理,因此无法应用任何事务方面。

AspectJ是不同的,因为它转换了TestServiceImpl的字节码,以便在各种方法调用中应用方面。

我不会说围绕内部方法调用应用方面不是正确的设计。您只需要知道它只适用于字节码检测。

答案 1 :(得分:1)

这是技术限制(正如其他人已经回答的那样)。如果你想让Spring检查一下,你可以像这样修改你的服务:

class TestServiceImpl implements TesService {

   TesService thiz; // setter left outside, assumed to be injected by Spring

   @Transactional
   void methodA() {
      thiz.methodB();
   }

   @Transactional(propagation=Propagation.NEVER)
      void methodB(){}

}

答案 2 :(得分:0)

这里的想法是最外层的方法知道什么是最好的整个交易。但是,正如你所注意到的,还有一些极端情况。

解决方法:将方法的实现移动到第二个bean并将该bean注入TestServiceImpl。由于您将获得注入代理,因此所有方法调用都将注意注释。

您需要拆分一些方法。如果您遇到这种情况:

methodX() {
     ...code before...
     methodB();
     ...code after...
}

你可以使用回调:

methodX() {
     Callable<Void> callback = new Callable<Void>() {
         Void call() {
              realImpl.methodB();
         }
     }
     realImpl.methodX(callback);
}

并在你的内部bean中:

void methodX(Callable<Void> callback) {
     ...code before...
     callback();
     ...code after...
}