【问题标题】:Spring aspects woven by AspectJ compiler working in Maven, but not in IntelliJ IDEA由 AspectJ 编译器编织的 Spring 方面在 Maven 中工作,但在 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。

标签: spring intellij-idea aspectj lombok aspectj-maven-plugin


【解决方案1】:

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

  1. 首先使用 Javac + Lombok 构建,然后在第二步中应用 AspectJ 二进制编织,全部在一个模块中。
  2. 与上述类似,但在模块 A 中执行第一个构建步骤,在单独的模块 B 中执行第二个构建步骤。然后您将拥有一个非编织和编织工件,您可以根据自己的喜好使用它们。例如,您还可以使用未编织的,并在启动应用程序时通过加载时编织 (LTW) 应用事务方面。有关方法 #1 和 #2,请参阅 my other answer here
  3. 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,直到我决定升级。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
  • 2017-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-02
相关资源
最近更新 更多