@Transactional 不适用于 AspectJ
@Transactional not applied with AspectJ
我决定使用 AspectJ
来避免无法从同一个 class
调用带有 @Transactional
注释的方法这一事实
所以我添加了这个配置:
@Configuration
@EnableTransactionManagement(mode= AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving(aspectjWeaving= AspectJWeaving.ENABLED)
public class App implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
在build.gradle
runtimeOnly("org.aspectj:aspectjweaver:1.9.7")
我 运行 应用程序(spring 嵌入了 tomcat 的启动应用程序)-javaagent:C:\xx\xx\.m2\repository\org\springframework\spring-instrument-5.3.12.jar
但是当我尝试
public void m1() {
this.m2()
}
@Transactional(propagation = Propagation.REQUIRED)
public void m2() {
....
}
似乎m2()
方法没有在事务中执行,
在我使用这些日志记录级别进行调试时的日志中:
logging.level.org.springframework.transaction.interceptor=trace
logging.level.org.springframework.orm.jpa=trace
没有这样的行:
Creating new transaction with name [xxx.m2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
我是不是遗漏了什么?
我克隆了您的 GitHub 项目,然后添加了
implementation 'org.springframework:spring-instrument'
您还应该将 Spring(事务)方面范围限制为仅编织您自己的应用程序 classes,以避免在尝试编织时出现大量 [Xlint:cantFindType]
消息 Spring 自己的 classes。您可以通过提供自己的 src/main/resources/org/aspectj/aop.xml
文件来执行此操作,如下所示:
<!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>
编织器选项还可以更轻松地查看哪些方面被编织到哪些连接点中,例如在启动期间你会看到
[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)
证明FooService.m2()
确实在编织
对于不那么“嘈杂”的 AspectJ 编织器,只需使用 <weaver options="-showWeaveInfo -Xlint:ignore">
- 不再有关于哪些方面已注册的警告或信息,但仍然有关于编织连接点的信息,这很重要,IMO。
然后我使用 Spring 工具和 AspectJ 编织器代理启动应用程序。只有前者不足以启动仪器,我需要两个代理。因为在JDK 16+需要打开java.lang
包到未命名的模块才能应用LTW,我在最近的JDK上测试,我也添加了相应的--add-opens
选项(在 JDK 15 之前不需要):
--add-opens java.base/java.lang=ALL-UNNAMED
-javaagent:.../aspectjweaver-1.9.7.jar
-javaagent:.../spring-instrument-5.3.12.jar
然后一切如预期的那样工作:
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
更新: 对于开箱即用的 Spring (Boot) 带来的问题,我真的很抱歉,但是现在,你要么使用 AspectJ 核心转储文件 ajdump.*.txt
- 交易方面编织仍然有效,就像我之前说的 - 或者使用你自己的 aop.xml
文件(见上文)。作为包含您自己的用于方面编织的应用程序基础包的替代方法,您也可以采取相反的方向排除 classes 或导致核心转储的包。在 Spring Boot 2.5.6 中,您只需添加
<exclude within="org.springframework.boot.jdbc.DataSourceBuilder.OraclePoolDataSourceProperties"/>
在 Spring Boot 2.3.3 中,AspectJ 抱怨这个 class:
<exclude within="org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer"/>
我认为这需要在 Spring Boot 或 Spring Core、Spring-TX 或 Spring-Aspects 中修复,无论这些方面位于何处.
更新 2: 我创建了 Spring Core issue #27650 以跟踪 AspectJ 核心转储问题。这不是你原来问题的根本原因,因为交易方面编织无论如何都有效,但无论如何都需要在 Spring(可能在 AspectJ)中解决。
我的理解是,如果您有一个 bean fooService
,并且您在 fooService
上调用了一个方法 m1
,并且不会创建事务,因此不会创建任何事务当您调用另一个方法时,在本例中为 m2
,这是预期的行为。
如果您以相反的方式进行操作,则会创建一个交易,如:m2
调用 m1
。我只知道这一点,因为我们在项目中遇到了同样的问题,这非常令人困惑。不确定为什么会这样,我尝试查找有关此的文档,如果找到它,我会将其添加到我的答案中。总之,事务只能通过在 bean 外部调用 bean 上的 @Transactional
方法来创建,而不能由 bean 本身通过另一个非事务性方法来创建。
我决定使用 AspectJ
来避免无法从同一个 class
@Transactional
注释的方法这一事实
所以我添加了这个配置:
@Configuration
@EnableTransactionManagement(mode= AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving(aspectjWeaving= AspectJWeaving.ENABLED)
public class App implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
在build.gradle
runtimeOnly("org.aspectj:aspectjweaver:1.9.7")
我 运行 应用程序(spring 嵌入了 tomcat 的启动应用程序)-javaagent:C:\xx\xx\.m2\repository\org\springframework\spring-instrument-5.3.12.jar
但是当我尝试
public void m1() {
this.m2()
}
@Transactional(propagation = Propagation.REQUIRED)
public void m2() {
....
}
似乎m2()
方法没有在事务中执行,
在我使用这些日志记录级别进行调试时的日志中:
logging.level.org.springframework.transaction.interceptor=trace
logging.level.org.springframework.orm.jpa=trace
没有这样的行:
Creating new transaction with name [xxx.m2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
我是不是遗漏了什么?
我克隆了您的 GitHub 项目,然后添加了
implementation 'org.springframework:spring-instrument'
您还应该将 Spring(事务)方面范围限制为仅编织您自己的应用程序 classes,以避免在尝试编织时出现大量 [Xlint:cantFindType]
消息 Spring 自己的 classes。您可以通过提供自己的 src/main/resources/org/aspectj/aop.xml
文件来执行此操作,如下所示:
<!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>
编织器选项还可以更轻松地查看哪些方面被编织到哪些连接点中,例如在启动期间你会看到
[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)
证明FooService.m2()
确实在编织
对于不那么“嘈杂”的 AspectJ 编织器,只需使用 <weaver options="-showWeaveInfo -Xlint:ignore">
- 不再有关于哪些方面已注册的警告或信息,但仍然有关于编织连接点的信息,这很重要,IMO。
然后我使用 Spring 工具和 AspectJ 编织器代理启动应用程序。只有前者不足以启动仪器,我需要两个代理。因为在JDK 16+需要打开java.lang
包到未命名的模块才能应用LTW,我在最近的JDK上测试,我也添加了相应的--add-opens
选项(在 JDK 15 之前不需要):
--add-opens java.base/java.lang=ALL-UNNAMED
-javaagent:.../aspectjweaver-1.9.7.jar
-javaagent:.../spring-instrument-5.3.12.jar
然后一切如预期的那样工作:
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
更新: 对于开箱即用的 Spring (Boot) 带来的问题,我真的很抱歉,但是现在,你要么使用 AspectJ 核心转储文件 ajdump.*.txt
- 交易方面编织仍然有效,就像我之前说的 - 或者使用你自己的 aop.xml
文件(见上文)。作为包含您自己的用于方面编织的应用程序基础包的替代方法,您也可以采取相反的方向排除 classes 或导致核心转储的包。在 Spring Boot 2.5.6 中,您只需添加
<exclude within="org.springframework.boot.jdbc.DataSourceBuilder.OraclePoolDataSourceProperties"/>
在 Spring Boot 2.3.3 中,AspectJ 抱怨这个 class:
<exclude within="org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer"/>
我认为这需要在 Spring Boot 或 Spring Core、Spring-TX 或 Spring-Aspects 中修复,无论这些方面位于何处.
更新 2: 我创建了 Spring Core issue #27650 以跟踪 AspectJ 核心转储问题。这不是你原来问题的根本原因,因为交易方面编织无论如何都有效,但无论如何都需要在 Spring(可能在 AspectJ)中解决。
我的理解是,如果您有一个 bean fooService
,并且您在 fooService
上调用了一个方法 m1
,并且不会创建事务,因此不会创建任何事务当您调用另一个方法时,在本例中为 m2
,这是预期的行为。
如果您以相反的方式进行操作,则会创建一个交易,如:m2
调用 m1
。我只知道这一点,因为我们在项目中遇到了同样的问题,这非常令人困惑。不确定为什么会这样,我尝试查找有关此的文档,如果找到它,我会将其添加到我的答案中。总之,事务只能通过在 bean 外部调用 bean 上的 @Transactional
方法来创建,而不能由 bean 本身通过另一个非事务性方法来创建。