Spring 方面由 AspectJ 编译器在 Maven 中编织,但不在 IntelliJ IDEA 中编织

Spring aspects woven by AspectJ compiler working in Maven, but not in IntelliJ IDEA

我正在使用 Spring boot 2.5.5 和 AspectJ 1.9.7 (CTW)。我发现有时事务不会回滚,要解决这个问题,我只需要重新编译代码并再次 运行 即可。例如:

我有方法 addB() 持久实体 B,方法 addC() 抛出异常和方法 A() 组合它们。当我调用 A() 时,抛出异常,但实体 B 保留在数据库中(如预期的那样)。当我用 @Transactional 注释方法 A() 时,结果是一样的。但是,如果我重新构建所有内容(没有任何更改),那么事务将被回滚并且数据库中没有新记录。

这是我的完整 POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.abcc</groupId>
    <artifactId>abcc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>abcc</name>
    <description>abcc</description>
    <properties>
        <java.version>11</java.version>
        <log4j2.version>2.15.0</log4j2.version>
        <aspectj.version>1.9.7</aspectj.version>
        <aspectj-maven-plugin.version>1.14.0</aspectj-maven-plugin.version>
    </properties>
    <dependencies>
        <!--SPRING-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--CACHE-->
        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!--DATABASE-->
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.1</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.passay</groupId>
            <artifactId>passay</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.sanctionco.jmail</groupId>
            <artifactId>jmail</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.13</version>
        </dependency>
        <!--AOP-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <activatedProperties>dev</activatedProperties>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>test</id>
            <properties>
                <activatedProperties>test</activatedProperties>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>com.h2database</groupId>
                    <artifactId>h2</artifactId>
                    <version>1.4.191</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                    <excludeDevtools>false</excludeDevtools>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <trimStackTrace>false</trimStackTrace>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>${aspectj-maven-plugin.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <Xlint>ignore</Xlint>
                    <verbose>true</verbose>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <complianceLevel>${java.version}</complianceLevel>
                    <encoding>utf-8</encoding>
                    <outxml>true</outxml>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <excludes>
                        <exclude>**/*.java</exclude>
                    </excludes>
                    <forceAjcCompile>true</forceAjcCompile>
                    <sources/>
                    <weaveDirectories>
                        <weaveDirectory>${project.build.outputDirectory}</weaveDirectory>
                    </weaveDirectories>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

情况很奇怪,我找不到相关信息。你能帮忙找原因吗?

我无法重现问题,因为 IDEA 没有找到 Lombok setter。即使在 运行 Maven 之前委派构建操作,我也会得到 NoSuchMethodError: '...TestEntity.setCode(java.lang.String)'。接下来,我将尝试不使用 Lombok。请注意,Lombok 和 AspectJ 彼此配合不好,请参阅 my answer here。或者,您也可以确保 Maven 执行以下任一操作:

  1. 首先使用 Javac + Lombok 构建,然后在第二步中应用 AspectJ 二进制编织,全部在一个模块中。
  2. 与上面类似,但是在模块 A 中执行第一个构建步骤,在单独的模块 B 中执行第二个构建步骤。然后您将得到一个 unwoven 和一个 woven 工件,您可以根据自己的喜好使用它们。例如,您还可以使用未编织的并在启动应用程序时通过加载时编织 (LTW) 应用事务方面。请参阅 了解方法 #1 和 #2。
  3. Delombok 源代码在第二个构建步骤中使用 AspectJ 编译器构建生成的源。

我在 IDE 中生成了构造函数、getter 和 setter,而不是使用 Lombok。现在项目在 IDE 和 Maven 中编译。它的行为完全正常。使用 @Transactional,创建 0 个实体,没有创建 2.

我不确定 Lombok 与 AspectJ 是否真的是使用 Lombok 注释时由于不可编译性而导致的问题,但是在没有 Lombok 的情况下尝试应该很容易。如果它在您的上下文中也有效,我们找到了罪魁祸首,并且可以考虑实施上述 3 种方法之一。那你做起来有什么困难可以告诉我。


更新: 我创建了双模块版本 - Javac + Lombok,然后是 Aspect 编织 - 在我的 fork 中为你发布 pull request #1.我还稍微提高了可测试性。看看是否适合你。

警告: 您不能简单地从 application-lombok 模块中 运行 DemoApplication,因为该模块仍未编织并且不会显示事务行为。但是您可以简单地将 运行 配置的类路径更改为 application-aspectj 模块:


更新: 正如我们在另一个答案的评论部分中发现的那样,除了有问题的 Lombok 与 AspectJ 编译器配置外,OP 还存在一个问题his IDE: 使用IntelliJ IDEA Community Edition,他先是不知道,然后无法安装AspectJ插件,也就是说IDEA不知道AspectJ编译器的antyhing和简单地覆盖之前可能由 AspectJ Maven 编译的任何内容 Java 类。因此,交易方面也不起作用,除非

  • 预运行编译被禁用并且mvn compile作为相应运行配置的附加预构建步骤启动,
  • 或者项目的所有构建操作都通过配置委托给 Maven,
  • OP 购买了 IDEA Ultimate 的许可证并安装了 AspectJ 插件。

首先:您的多模块方法在我的环境中完美运行。但是后来我检查了初始 MCVE,当我完全删除 Lombok 时,奇怪的行为并没有消失。在阅读您的答案时 () 我在 IntelliJ 设置 (Build Tools -> Maven -> Runner) 它开始正常工作。在下一步中,我关闭了此选项并在 运行 配置中选中了“Do not build before 运行”。我不完全理解它(特别是为什么它在第二次尝试后以旧方式工作),但你的评论帮助我达到了那个目的。

我将研究 IntelliJ 行为(在两种情况下,控制台输出几乎相同),但如果您知道为什么它会那样工作,我会很高兴听到它。你帮了我大忙,谢谢!

解决方案总结: 我在我的应用程序的 IntelliJ 运行 配置中启用了“不要在 运行 之前构建”。现在更改在第一次构建后生效。