首页 > 解决方案 > @Transactional 不适用于 AspectJ

问题描述

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

我运行应用程序(嵌入tomcat的spring boot应用程序)-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

我在那里错过了什么吗?

标签: javaspringspring-boothibernateaspectj

解决方案


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

implementation 'org.springframework:spring-instrument'

您还应该将 Spring(事务)方面的范围限制为仅编织您自己的应用程序类,以避免[Xlint:cantFindType]在尝试编织 Spring 自己的类时出现大量消息。您可以通过提供自己的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 weaver 代理启动了应用程序。只有前者不足以启动仪器,我需要两个代理。因为在 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 问题 #27650以跟踪 AspectJ 核心转储问题。这不是您最初问题的根本原因,因为事务方面编织无论如何都可以工作,但无论如何都需要在 Spring(并且可能在 AspectJ 中)解决它。


推荐阅读