Search code examples
javaaopaspectj

AspectJ 1.9.4 with OpenJDK 11 Without Spring Context not working as a dependent module


I am trying to create custom loadtime annotations with AspectJ, Open JDK11 without Spring Context. It works fine within a module and annotations are weaving at class load time and aspects are executing at runtime. No issues, But when aspectJ implemented module added as a dependency on another module. AspectJ and annotations are not processing. Am I missing any configuration?

module-a

@Documented
@Inherited
@Target(METHOD)
@Retention(RUNTIME)
public @interface Counter {
    String name() default "";
}
@Aspect
public class CounterAspect {
    @Around("execution(* *.*(..)) && @annotation(counter)")
    public void myBeforeLogger(ProceedingJoinPoint joinPoint, Counter counter) {

        System.out.println("Okay - we're in the before handler...");
        System.out.println("The test annotation value is: " + counter.name().toString());
}

resources/META-INF/aop.xml

<aspectj>
    <aspects>
        <aspect name="CounterAspect"/>
    </aspects>
    <weaver options="-verbose">
        <!-- weave anything -->
        <include within="*" />
    </weaver>
</aspectj>

pom.xml

<properties>
    <aspectj.version>1.9.4</aspectj.version>
</properties>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
</dependency>
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.11</version>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.9</version>
            <configuration>
                <argLine>
                -javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
                </argLine>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

module-b

pom.xml

<dependency>
    <groupId>org.mymodule</groupId>
    <artifactId>module-a</artifactId>
    <version>1.0.0</version>
</dependency>
public class MyCounter {
    @Counter(name="call_count")
    public void count() {}
}
public class MyCounterTest {
    @Test
    public void testCount() {
        MyCounter counter = new MyCounter();
        counter.count();
        //NOTE: I expect CounterAspect from module-a should be intercepted. It is not working!!!! There is no aspectJ class loading verbose in console.
    }
}

Solution

  • I suggest you create a parent POM with some predefined properties, dependency versions and plugin configurations. Your two sub-modules (one for application + test, one for aspect) can use these definitions, which leads to shorter sub-module POMs:

    <?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>de.scrum-master</groupId>
      <artifactId>aspectj-ltw-test-multi-module</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>pom</packaging>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.source-target.version>11</java.source-target.version>
        <aspectj.version>1.9.4</aspectj.version>
      </properties>
    
      <build>
        <pluginManagement>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.3</version>
              <configuration>
                <source>${java.source-target.version}</source>
                <target>${java.source-target.version}</target>
                <!-- IMPORTANT -->
                <useIncrementalCompilation>false</useIncrementalCompilation>
              </configuration>
            </plugin>
            <plugin>
              <groupId>com.nickwongdev</groupId>
              <artifactId>aspectj-maven-plugin</artifactId>
              <version>1.12.1</version>
              <configuration>
                <!--<showWeaveInfo>true</showWeaveInfo>-->
                <source>${java.source-target.version}</source>
                <target>${java.source-target.version}</target>
                <Xlint>ignore</Xlint>
                <complianceLevel>${java.source-target.version}</complianceLevel>
                <encoding>${project.build.sourceEncoding}</encoding>
                <!--<verbose>true</verbose>-->
                <!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn>-->
              </configuration>
              <executions>
                <execution>
                  <!-- IMPORTANT -->
                  <phase>process-sources</phase>
                  <goals>
                    <goal>compile</goal>
                    <goal>test-compile</goal>
                  </goals>
                </execution>
              </executions>
              <dependencies>
                <dependency>
                  <groupId>org.aspectj</groupId>
                  <artifactId>aspectjtools</artifactId>
                  <version>${aspectj.version}</version>
                </dependency>
                <dependency>
                  <groupId>org.aspectj</groupId>
                  <artifactId>aspectjweaver</artifactId>
                  <version>${aspectj.version}</version>
                </dependency>
              </dependencies>
            </plugin>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.9</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
          <dependency>
            <groupId>de.scrum-master</groupId>
            <artifactId>aspect</artifactId>
            <version>1.0-SNAPSHOT</version>
          </dependency>
          <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
    
      <modules>
        <module>aspect</module>
        <module>application</module>
      </modules>
    
    </project>
    

    Please note that I am not using the original AspectJ Maven plugin but a fork which supports Java 11. The original still lags behind. As you said you want to use Java 11, you have to do that. With Java 8 you can use the original plugin.

    Aspect module:

    <?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>
    
      <parent>
        <groupId>de.scrum-master</groupId>
        <artifactId>aspectj-ltw-test-multi-module</artifactId>
        <version>1.0-SNAPSHOT</version>
      </parent>
    
      <groupId>de.scrum-master</groupId>
      <artifactId>aspect</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <build>
        <plugins>
          <plugin>
            <groupId>com.nickwongdev</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
          </plugin>
        </plugins>
      </build>
    
      <dependencies>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
        </dependency>
      </dependencies>
    </project>
    
    <aspectj>
      <aspects>
        <aspect name="de.scrum_master.aspect.CounterAspect"/>
      </aspects>
      <weaver options="-verbose">
        <!-- weave anything -->
        <include within="*"/>
      </weaver>
    </aspectj>
    
    package de.scrum_master.aspect;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    @Documented
    @Inherited
    @Target(METHOD)
    @Retention(RUNTIME)
    public @interface Counter {
      String name() default "";
    }
    
    package de.scrum_master.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    
    @Aspect
    public class CounterAspect {
      @Around("execution(* *.*(..)) && @annotation(counter)")
      public void myBeforeLogger(ProceedingJoinPoint joinPoint, Counter counter) {
        System.out.println(joinPoint + " -> " + counter.name());
      }
    }
    

    Application module:

    <?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>
    
      <parent>
        <groupId>de.scrum-master</groupId>
        <artifactId>aspectj-ltw-test-multi-module</artifactId>
        <version>1.0-SNAPSHOT</version>
      </parent>
    
      <groupId>de.scrum-master</groupId>
      <artifactId>application</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <dependencies>
        <dependency>
          <groupId>de.scrum-master</groupId>
          <artifactId>aspect</artifactId>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
              <argLine>
                -javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
              </argLine>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    
    package de.scrum_master.app;
    
    import de.scrum_master.aspect.Counter;
    
    public class MyCounter {
      @Counter(name = "call_count")
      public void count() {}
    }
    
    package de.scrum_master.app;
    
    import org.junit.Test;
    
    public class MyCounterTest {
      @Test
      public void testCount() {
        new MyCounter().count();
      }
    }
    

    Now if you run mvn clean verify or just mvn clean test you will get the following console log for the application module test:

    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    [AppClassLoader@2aae9190] info AspectJ Weaver Version 1.9.4 built on Friday May 10, 2019 at 08:43:10 PDT
    [AppClassLoader@2aae9190] info register classloader jdk.internal.loader.ClassLoaders$AppClassLoader@2aae9190
    [AppClassLoader@2aae9190] info using configuration (...)/SO_AJ_LTWTestMultiModule_56397605/aspect/target/classes/META-INF/aop.xml
    [AppClassLoader@2aae9190] info register aspect de.scrum_master.aspect.CounterAspect
    [IsolatedClassLoader@74e52ef6] info AspectJ Weaver Version 1.9.4 built on Friday May 10, 2019 at 08:43:10 PDT
    [IsolatedClassLoader@74e52ef6] info register classloader org.apache.maven.surefire.booter.IsolatedClassLoader@74e52ef6
    [IsolatedClassLoader@74e52ef6] info using configuration (...)/SO_AJ_LTWTestMultiModule_56397605/aspect/target/classes/META-INF/aop.xml
    [IsolatedClassLoader@74e52ef6] info register aspect de.scrum_master.aspect.CounterAspect
    Running de.scrum_master.app.MyCounterTest
    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access using Lookup on org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor (file:/(...)/.m2/repository/org/aspectj/aspectjweaver/1.9.4/aspectjweaver-1.9.4.jar) to class java.lang.ClassLoader
    WARNING: Please consider reporting this to the maintainers of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor
    WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    WARNING: All illegal access operations will be denied in a future release
    [AppClassLoader@2aae9190] info processing reweavable type de.scrum_master.aspect.CounterAspect: de\scrum_master\aspect\CounterAspect.aj
    [AppClassLoader@2aae9190] info successfully verified type de.scrum_master.aspect.CounterAspect exists.  Originates from de\scrum_master\aspect\CounterAspect.aj
    execution(void de.scrum_master.app.MyCounter.count()) -> call_count
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.885 sec
    

    Please especially note

    execution(void de.scrum_master.app.MyCounter.count()) -> call_count
    

    This is what you want, the load-time-woven aspect kicks in for your test.


    Update: I created a GitHub repository for you. You can just clone it instead of copying all my code snippets from this answer.