由 AspectJ 编译器编织的 Spring 方面在 Maven 中工作,但在 IntelliJ IDEA 中没有

Posted

技术标签:

【中文标题】由 AspectJ 编译器编织的 Spring 方面在 Maven 中工作,但在 IntelliJ IDEA 中没有【英文标题】:Spring aspects woven by AspectJ compiler working in Maven, but not in IntelliJ IDEA 【发布时间】:2022-01-22 22:46:48 【问题描述】:

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

我有方法 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>

这是非常奇怪的情况,我找不到相关信息。你能帮忙找出原因吗?

【问题讨论】:

如果我在 GitHub 上有 MCVE 以重现问题,我会很乐意提供帮助。我已经知道这里可能会发生什么,但我想亲眼看看而不是猜测。让您的 MCVE 变得非常简单,可以不使用数据库,也可以使用自动启动和初始化的内存中的数据库。顺便说一句,您是在谈论必须重复的 Maven 构建还是 IDE 构建? Maven 构建,通过 IntelliJ 2021.2.3 中的运行配置启动。带有配置文件“dev”的命令“clean install -DskipTests -T 6”。我会在评论中提供 MCVE。 这里是 MCVE:github.com/Shibitos/MCVE-jv21122021 【参考方案1】:

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

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

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

我不确定 Lombok 与 AspectJ 是否真的是由于使用 Lombok 注释时的不可编译性而导致的问题,但在没有 Lombok 的情况下尝试应该很容易。如果它也适用于您的环境,我们找到了罪魁祸首,可以考虑实施上述 3 种方法之一。然后你可以告诉我你这样做是否有任何困难。


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

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


更新:正如我们在另一个答案的评论部分中发现的那样,除了有问题的 Lombok 与 AspectJ 编译器配置之外,OP 的 IDE 也存在问题:使用 IntelliJ IDEA 社区版,他首先不知道,然后无法安装 AspectJ 插件,这意味着 IDEA 不了解 AspectJ 编译器,只是简单地用纯 Java 类覆盖之前可能由 AspectJ Maven 编译的任何内容。因此,事务方面也不起作用,除非

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

【讨论】:

请注意我关于如何使用正确的模块从 IDEA 运行演示应用程序的更新。 更新 2: 我解释了社区版缺少 AspectJ 插件的 IntelliJ IDEA 问题,以便全面了解这个问题中的两个问题。【参考方案2】:

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

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

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

【讨论】:

好吧,如果您必须禁用构建,则表明构建配置有问题。您应该修复它而不是禁用它。此外,就像我说的那样,由于在 Maven 自动导入上下文中存在问题的 Lombok 情况,我无法重现原始问题。所以我猜你没有使用 Maven 自动导入并且有某种不是从 Maven 派生的手动构建配置。因此,您在这里遇到了 IDEA 问题。但是你原来的 Maven 项目也坏了。由于 Lombok + AspectJ,构建失败。 我推送了没有 Lombok 的版本,问题仍然存在。我怎样才能按你的方式检查?现在我正在使用 IntelliJ 打开项目,使用命令行添加 Maven 运行配置:“clean install -DskipTests”,然后使用应用程序运行配置运行它。即使我使用“mvnw spring-boot:run”从控制台构建和运行它,一切正常。唯一的情况是当我使用具有默认设置的应用程序运行配置时。将构建/运行操作委托给 maven 也可以解决问题。 我可以重现您的问题的唯一方法是手动选择 Javac 而不是 Ajc“文件 | 设置 | 构建、执行、部署 | 编译器 | Java 编译器” 中。通常这应该在 Maven 自动导入期间正确设置。您是否忘记为 IntelliJ IDEA 安装 AspectJ 插件? 当然,IDE 将始终编译 POJO 而不会编织任何方面。在这种情况下,您确实必须为您的运行配置禁用自动编译,并始终首先使用 Maven 构建。或者您将构建委托给 Maven。但最好只安装插件。 我不知道这个插件(我刚开始学习 AspectJ)。不幸的是,我使用的是 IntelliJ 的社区版本,this 插件需要终极版本。似乎 Ajc 也只能在终极版中使用。我想我必须坚持使用纯 maven,直到我决定升级。

以上是关于由 AspectJ 编译器编织的 Spring 方面在 Maven 中工作,但在 IntelliJ IDEA 中没有的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring dm Server 1.x 中使用 EclipseLink JPA 时出现 Aspectj 加载时间编织问题

AOP:选择正确的时机进行编织

AspectJ 可以通过 sun.net.* 包编织吗?

aop

AWS SWF 流框架 - Eclipse AspectJ 加载时编织

AspectJ:如何限制目标的哪些方面