如何使用编译编织从项目的 src 目录中的测试目录强制执行 AspectJ 搜索?

How to force AspectJ search from test directory in src directory of project using compile weaving?

实际上,这将是一个更复杂的问题。 我只想在测试目的中使用 AspectJ 。 已找到建议 to use if() JointPoint 和一些静态布尔字段。 此外,首先我开始使用方面作为我的基本测试方法的内部静态 class。 经过一些实验,我将其替换为自己的class,但实际上并没有得到我想要的结果。 所以,我只是创建了一些测试项目。 Maven pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Testing</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <mockito.version>3.11.2</mockito.version>
        <aspectj.version>1.9.7</aspectj.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <dependencies>
                    <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-surefire-provider
-->
                    <dependency>
                        <groupId>org.junit.platform</groupId>
                        <artifactId>junit-platform-surefire-provider</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                </dependencies>
                <!--<configuration>
                    <argLine>-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar</argLine>
                </configuration>-->
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>${maven.compiler.source}</complianceLevel>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <!-- use this goal to weave all your main classes -->
                            <goal>compile</goal>
                            <!-- use this goal to weave all your test classes -->
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

类: 答:

package classes;

public class A {
    private String a = "classes.A";

    public String getA()
    {
        return a;
    }

    public String getFromB()
    {
        return new B().getB();
    }
}

乙:

package classes;

public class B {
    private String b = "classes.B";

    public String getB() {
        return b;
    }
}

测试class:

package aspectj;

import classes.A;
import classes.B;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;


public class NewTest {
    private static boolean useAspect = false;

    public static boolean isUseAspect() {
        return useAspect;
    }

    @BeforeEach
    void init()
    {
        useAspect = true;
    }

    @Test
    public void changeValue()
    {
        B b = new B();
        System.out.println(b.getB());
    }

    @Test
    public void changeValueInA()
    {
        A a = new A();
        System.out.println(a.getFromB());
    }
}

看点class

package aspectj;

import org.aspectj.lang.Aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AspectB {

    @Pointcut("if()")
    public static boolean useAspect()
    {
        return NewTest.isUseAspect();
    }

    @Pointcut("call(* classes.B.getB())")
    public void callTestMethod() {}


    @Around("callTestMethod()")
    public String myAdvice(ProceedingJoinPoint point) throws Throwable {
        return "You have been hacked!";
    }
}

主要class:

package classes;

public class TestOutputHere {
    public static void main(String[] args) {
        System.out.println(new A().getFromB());
    }
}

我在 运行 测试和主要方法之后得到了结果:

第二个结果不适合我...所以经过一些实验并删除 AspectJ 依赖项的测试范围,删除 if() JointPoint(我们不能使用来自 src 的测试 classes)并放置 Aspect class 在 src 中我得到了结果:

色欲结果也对我不利。而且我真的不想对所有项目都使用方面。 在那之后,我只是尝试使用一些加载时间编织来配置 maven surefire 插件:

<configuration>
 <argLine>
-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
</argLine>
</configuration>

我得到了我想要的结果:

所以,这些字母后的问题在哪里?) 问题是:

  1. 我可以通过编译织入而不使用 AspectJ 得到这个结果吗 class加载程序?
  2. 因为我在实际项目中有性能限制 - AspectJ 如何 class在这种情况下,加载程序会影响非测试环境的性能吗?
  3. 在我描述的加载时编织的情况下 - 所有 classes 项目将由 AspectJ 重新编译?只是测试?重新编译如何在加载时工作?

我会非常感谢这个答案!

你的代码有几个问题:

  • 您在每次测试前设置 useAspect = true,但在测试结束后永远不会重置为 false。这会将上下文泄漏到您希望方面处于非活动状态的其他测试中。你应该把它清理干净。

  • 方面有一个 if() 切入点,具体取决于测试 class 中的静态方法。在正常情况下,测试 class 在应用程序运行期间不可用。静态字段及其访问器方法(如果有的话)应该在方面 class 本身。

    package aspectj;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class AspectB {
      private static boolean useAspect = false;
    
      public static void setUseAspect(boolean useAspect) {
        AspectB.useAspect = useAspect;
      }
    
      @Pointcut("call(* classes.B.getB()) && if()")
      public static boolean callTestMethod() {
        return useAspect;
      }
    
      @Around("callTestMethod()")
      public String myAdvice(ProceedingJoinPoint point) throws Throwable {
        return "You have been hacked!";
      }
    }
    
    package aspectj;
    
    import classes.A;
    import classes.B;
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    
    public class NewTest {
      @BeforeEach
      void init() {
        AspectB.setUseAspect(true);
      }
    
      @AfterEach
      void cleanUp() {
        AspectB.setUseAspect(false);
      }
    
      @Test
      public void changeValue() {
        B b = new B();
        System.out.println(b.getB());
      }
    
      @Test
      public void changeValueInA() {
        A a = new A();
        System.out.println(a.getFromB());
      }
    }
    
  • 可能,你的 aspect 是在 src/test/java 而不是 src/main/java 中定义的,这解释了为什么它只被编译到测试 classes 中而不是应用程序 classes。但后者是您所期望的,如果从一个应用程序 class 到另一个应用程序的方法调用应该被拦截。因此,您需要将方面移至主要来源,并使 aspectjrt 具有编译范围,而不是测试范围。

但在这种情况下,方面应该只影响测试,我建议不要使用编译时织入 (CTW),因为这意味着应用程序始终需要 AspectJ 运行时 class 路径(见上文),即使方面是不活动的。 CTW 只有在至少有时在应用程序运行时期间该方面也应该处于活动状态时才有意义。即便如此,加载时编织(LTW)是否可能不是更好的解决方案仍值得商榷,例如如果它是一个很少使用的调试方面。 CTW 是生产方面的理想选择。在这种情况下,很明显 LTW 使用 Java 代理是正确的方法。就像你说的,你甚至不需要丑陋的静态字段和 if() 切入点。