I decide to use AspectJ
to avoid the fact that a method annotated with @Transactional
can't be invoked from the same class
So I add this configuration :
@Configuration
@EnableTransactionManagement(mode= AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving(aspectjWeaving= AspectJWeaving.ENABLED)
public class App implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
In build.gradle
runtimeOnly("org.aspectj:aspectjweaver:1.9.7")
And I run the app (spring boot app with tomcat embedded) with -javaagent:C:\xx\xx\.m2\repository\org\springframework\spring-instrument-5.3.12.jar
But when I try
public void m1() {
this.m2()
}
@Transactional(propagation = Propagation.REQUIRED)
public void m2() {
....
}
It seems that m2()
method is not executed within an transaction,
In the logs when I debug with these logging level :
logging.level.org.springframework.transaction.interceptor=trace
logging.level.org.springframework.orm.jpa=trace
there is no line such :
Creating new transaction with name [xxx.m2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Am I missing something there ?
CodePudding user response:
I cloned your GitHub project, then added
implementation 'org.springframework:spring-instrument'
You should also limit the Spring (transaction) aspect scope to only weave your own application classes in order to avoid lots of [Xlint:cantFindType]
messages when trying to weave Spring's own classes. You can do this by providing your own src/main/resources/org/aspectj/aop.xml
file as follows:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<!-- You can also add -Xlint:ignore in order to avoid lots of '[Xlint:cantFindType]' warnings -->
<weaver options="-verbose -showWeaveInfo">
<!-- Only weave classes in our application-specific packages -->
<include within="com.example.aspectj..*"/>
</weaver>
</aspectj>
The weaver options also make it easier to see which aspects are woven into which joinpoints, e.g. during start-up you see
[AppClassLoader@77556fd] weaveinfo Join point 'method-execution(void com.example.aspectj.services.FooService.m2())' in Type 'com.example.aspectj.services.FooService' (FooService.java:27) advised by around advice from 'org.springframework.transaction.aspectj.JtaAnnotationTransactionAspect' (AbstractTransactionAspect.aj:67)
which proves that FooService.m2()
is actually being woven.
For a less "noisy" AspectJ weaver, simply use <weaver options="-showWeaveInfo -Xlint:ignore">
- no more warnings or info about which aspects are registered, but still the information about woven joinpoints, which is important to see, IMO.
Then I started the application with both the Spring instrumentation and AspectJ weaver agents. Only the former was not enough to kick off instrumentation, I needed both agents. Because on JDK 16 you need to open the java.lang
package to the unnamed module in order to be able to apply LTW and I was testing on a recent JDK, I also added the corresponding --add-opens
option (not necessary until JDK 15):
--add-opens java.base/java.lang=ALL-UNNAMED
-javaagent:.../aspectjweaver-1.9.7.jar
-javaagent:.../spring-instrument-5.3.12.jar
Then everything works es expected:
o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
com.example.aspectj.AspectjApplication : Started AspectjApplication in 7.339 seconds (JVM running for 10.046)
com.example.aspectj.AspectjApplication : running ..
com.example.aspectj.services.FooService : m1 : called
o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.example.aspectj.services.BooService.m3]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(1516190088<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@543f6ccb]
com.example.aspectj.services.BooService : m3 : called
o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(1516190088<open>)]
o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(1516190088<open>)] after transaction
o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.example.aspectj.services.FooService.m2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(45178615<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@20e4fce0]
com.example.aspectj.services.FooService : m2 : called
o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(45178615<open>)]
o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(45178615<open>)] after transaction
Update: I am really sorry for problems that come with Spring (Boot) out of the box, but as it is now, you either have to live with the AspectJ core dump files ajdump.*.txt
- the transaction aspect weaving still works, like I said before - or use your own aop.xml
file (see above). As an alternative to including your own application base package for aspect weaving, you can also go the opposite direction of excluding the classes or packages causing the core dumps. In Spring Boot 2.5.6, you simply add
<exclude within="org.springframework.boot.jdbc.DataSourceBuilder.OraclePoolDataSourceProperties"/>
In Spring Boot 2.3.3, AspectJ complains about this class:
<exclude within="org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer"/>
I think this needs to be fixed in Spring Boot or maybe in Spring Core, Spring-TX or Spring-Aspects, wherever those aspects are located.
Update 2: I have created Spring Core issue #27650 in order to track the AspectJ core dump problem. It is not the root cause of your original problem, because transaction aspect weaving works anyway, but it needs to be addressed in Spring (and possibly in AspectJ) anyway.
CodePudding user response:
My understanding is that if you have a bean fooService
, and you invoke a method m1
on fooService
, and that does not create a transaction so no transaction will be created when you call another method, in this case m2
and this is intended behavior.
If you did it the other way around this would create a transaction as in: m2
calling m1
. I only know this because we've had the same problem in our project, which was very confusing. Not sure why it's like this, I tried to look for the documentation on this, if I find it I'll add it to my answer. In summary, a transaction can only be created by calling an @Transactional
method on a bean, outside of that bean, but not by the bean itself by another method which is not transactional.