【问题标题】:@Transactional not applied with AspectJ@Transactional 不适用于 AspectJ
【发布时间】:2021-10-31 17:53:36
【问题描述】:

我决定使用AspectJ 来避免无法从同一个类中调用带有@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")

然后我使用-javaagent:C:\xx\xx\.m2\repository\org\springframework\spring-instrument-5.3.12.jar 运行应用程序(嵌入了 tomcat 的 Spring Boot 应用程序)

但是当我尝试时

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

我错过了什么吗?

【问题讨论】:

  • 被另一个Spring组件调用时是否执行?如果是这样,那么您的配置有问题,Spring 实际上是在应用它自己的 Spring AOP 而不是本机 AspectJ。无论哪种方式,GitHub 上的 MCVE 都会帮助我重现和解决您的问题。
  • @kriegaex 是的,当我在我的工作项目中调用另一个 bean 的 @Transactional 方法时,它被执行,但是当我在 github.com/soyousseflechqar/aspectj 上重现问题时,没有创建事务
  • 我不是Spring用户,只是AOP专家。但是您的示例项目在调用 @Transactional 方法时不会记录任何内容,无论是从 Spring bean 外部还是从内部调用它时。那么我如何验证您的说法,例如它不起作用?将fooService.m1(); 更改为fooService.m2();?在这两种情况下,都没有事务日志记录。也许你应该先准备好你的 MCVE 以实际重现问题。
  • @kriegaex 我只是忘了添加日志记录级别,但现在添加了 2 个分支:一个具有代理模式可以工作,另一个具有 aspectj 模式不工作。

标签: java spring spring-boot hibernate aspectj


【解决方案1】:

我克隆了你的 GitHub 项目,然后添加了

implementation 'org.springframework:spring-instrument'

您还应该将 Spring(事务)方面的范围限制为仅编织您自己的应用程序类,以避免在尝试编织 Spring 自己的类时出现大量 [Xlint:cantFindType] 消息。您可以通过提供自己的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 编织器,只需使用 &lt;weaver options="-showWeaveInfo -Xlint:ignore"&gt; - 不再有警告或有关注册哪些方面的信息,但仍然是有关编织连接点的信息,这很重要,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 文件(见上文)。作为包含您自己的应用程序基础包以进行方面编织的替代方法,您还可以采取相反的方向,即排除导致核心转储的类或包。在 Spring Boot 2.5.6 中,您只需添加

<exclude within="org.springframework.boot.jdbc.DataSourceBuilder.OraclePoolDataSourceProperties"/>

在 Spring Boot 2.3.3 中,AspectJ 抱怨这个类:

<exclude within="org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer"/>

我认为这需要在 Spring Boot 或 Spring Core、Spring-TX 或 Spring-Aspects 中修复,无论这些方面位于何处。


更新 2: 我创建了 Spring Core issue #27650 以跟踪 AspectJ 核心转储问题。这不是您最初问题的根本原因,因为事务方面编织无论如何都可以工作,但无论如何都需要在 Spring(并且可能在 AspectJ 中)解决它。

【讨论】:

  • 使用 JDK 11 添加 AspectJ weaver 代理后仍然无法正常工作
  • 事务只为m3(位于另一个bean 上的那个)创建,而不是为m2
  • 我注意到很多日志,比如 [AppClassLoader@77556fd] error can't determine implemented interfaces of missing type com.google.gson.Gson when weaving type org.springframework.http.converter.json.GsonHttpMessageConverter when weaving classes when weaving [Xlint:cantFindType] 用于不同的类
  • 我用JDK 11快速测试了一下,也没问题。我还看到很多[Xlint:cantFindType] 消息,但我想如果您没有使用相应的方面,它们可以被忽略。它们似乎不会妨碍事务管理,至少在我的工作站上不会。
  • 请注意我更新的答案。 com.oracle.database.jdbc:ojdbc8 不见了。相反,我做了正确(干净)的事情,添加了一个aop.xml 文件并限制了方面范围。看看这是否可以帮助您避免警告。
【解决方案2】:

我的理解是,如果您有一个 bean fooService,并且您在 fooService 上调用方法 m1,并且不会创建事务,因此当您调用另一个方法时不会创建事务,在此案例m2,这是预期行为。

如果您以相反的方式执行此操作,则会创建一个事务,如下所示:m2 调用 m1。我只知道这一点,因为我们在项目中遇到了同样的问题,这非常令人困惑。不知道为什么会这样,我试图查找有关此的文档,如果找到,我会将其添加到我的答案中。总之,事务只能通过调用 bean 上的 @Transactional 方法在该 bean 之外创建,但不能由 bean 本身通过另一个非事务性方法来创建。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多