我已经通过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设计有两种可能性,
使用spring是技术限制,他们无法检查methodB()中的注释,尽管它是公开的?但是如果AspectJ可以检查它,那么为什么不是Spring?
他们故意限制此内部方法调用的AOP检查。这种方法调用(其中目标方法使用不同的transactionPropagation注释)是否违反了正确的设计方法?
答案 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...
}