I have gone through number of post regarding tx with spring and AspectJ. Below is the summary,
Say, I have a service class and its interface
interface TestService {
void methodA();
void methodB();
}
class TestServiceImpl implements TesService {
@Transactional
void methodA() {
methodB();
}
@Transactional(propagation=Propagation.NEVER)
void methodB(){}
}
And my configuration
<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>
I am calling testService.methodA() from some client class. As per spring’s JDK dynamic proxy usage, it will only care about @Transactional on methodA(), but not @Transactional(propagation=Propagation.NEVER) on methodB(). So the code executes with proper transaction and commits. If we use AspectJ mode then it will also check for @Transactional(propagation=Propagation.NEVER) on methodB() and will throw an exception.
Now my question is, why this limitation is imposed by Spring? Now there are two possibilities for Spring design,
-
It is a technical limitaion with spring that they can not check annotation in methodB(), though it is public? But if AspectJ can check it, then why not Spring?
-
Intentionally they have limited this AOP checking for internal method calls. Is this kind of method call (where target method is annotated with different transactionPropagation) is against proper design methodology?
Yes, it’s a technical limitation. When you don’t use AspectJ, the transactional aspect is implemented by returning a proxy around the actual bean class instance and returning/injecting this proxy into other beans. So, when you call
testService.methodA(), the following (basically) happens:The proxy applies the transactional aspect around the call to
testServiceImpl.methodA(): it starts the transaction before, and commits/rollbacks it after.If you call
this.methodB()frommethodA(), the following happens:And since you bypass the proxy, no transactional aspect can be applied.
AspectJ is different because it transforms the byte-code of
TestServiceImplin order to apply aspects around various method invocations.I wouldn’t say that applying aspects around internal method calls is not proper design. You just need to be aware that it works only with byte-code instrumentation.