javagradleaspectjfreefair-aspectj

Aspect from external module (for Java + Gradle project) not working


I have put the MCVE code for this on GitHub - https://github.com/ravitechy/multi-module-project/tree/main

I have two modules in my Java project (built on Gradle) - app which depends on commons.

commons contain the Aspect class BasicCommonAspect as defined below

package org.example.commons.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import javax.annotation.PostConstruct;

@Aspect
@EnableAspectJAutoProxy
public class BasicCommonAspect {
           
    @Before("@annotation(bca) && execution(* *(..))")
    public void executeBeforeForCommonAnnotation(JoinPoint joinPoint, BasicCommonAnnotation bca) {
        System.out.println("BasicCommonAnnotation detected");
    }
}

commons also contain an annotation BasicCommonAnnotation like this

package org.example.commons.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BasicCommonAnnotation {
}

app contains AppBackground and has a method run annotated with BasicCommonAnnotation

package org.example;

import org.example.commons.aspect.BasicCommonAnnotation;

public class AppBackground {

    @BasicCommonAnnotation
    public void run() {
        System.out.println("Running in background");
    }

}

There is a driver class in app module named RunApp which invokes the run method in AppBackground

package org.example;

public class RunApp {

    public static void main(String[] args) {
        final AppBackground background = new AppBackground();
        background.run();
    }
}

I am using post-compile weaving (using the Gradle plugin io.freefair.aspectj.post-compile-weaving) since I need to use Project Lombok.

When I run the RunApp class, my aim is to see the method BasicCommonAspect.executeBeforeForCommonAnnotation() executed as per the advice defined.

From my preliminary research, I got to know that I need to include the external module as an aspect library in apps module. I found a Stack Overflow answer for the exact same problem here - AspectJ - Aspect from external JAR, but it was for the Maven build. I am looking for exactly similar configuration for my Gradle project.

I tried using inpath as suggested here https://github.com/freefair/gradle-plugins/issues/450, but I began running into different build errors now as indicated below.

Not skipping AJC (inpath is set)
Starting process 'command 'D:\OpenJdk17\bin\java.exe''. Working directory: D:\intellij-idea workspace\multi-module-project\app Command: D:\OpenJdk17\bin\java.exe -Dfile.encoding=windows-1252 -Duser.country=IN -Duser.language=en -Duser.variant -cp C:\Users\raviteja.kothapalli\.gradle\caches\modules-2\files-2.1\org.aspectj\aspectjtools\1.9.8\caf8b7f9023d93af2ed2e6e8bf023a800e9d1323\aspectjtools-1.9.8.jar org.aspectj.tools.ajc.Main -argfile D:\intellij-idea workspace\multi-module-project\app\build\tmp\compileJava\ajc.options
Successfully started process 'command 'D:\OpenJdk17\bin\java.exe''
←[0K
←[0K
←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [18s]←[m←[35D←[1B←[1m> :app:compileJava←[m←[18D←[1B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [19s]←[m←[35D←[2B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [20s]←[m←[35D←[2B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [21s]←[m←[35D←[2B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [22s]←[m←[35D←[2B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [23s]←[m←[35D←[2B←[2AC:\Users\raviteja.kothapalli\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot-autoconfigure\2.7.18\9cf147c6ca274c75b32556acdcba5a1de081ebcd\spring-boot-autoconfigure-2.7.18.jar [error] can't determine implemented interfaces of missing type org.springframework.web.server.WebExceptionHandler
when weaving type org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
when weaving classes
when weaving
when batch building BuildConfig[null] #Files=0 AopXmls=#0
 [Xlint:cantFindType]
(no source information available)
        [Xlint:cantFindType]
←[0K
←[0K
←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [23s]←[m←[35D←[1B←[1m> :app:compileJava←[m←[18D←[1B←[2A←[1m<←[0;32;1m===←[0;39;1m----------> 25% EXECUTING [24s]←[m←[35D←[2B←[2AC:\Users\raviteja.kothapalli\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot\2.7.18\f6dbdd8da7c2bded63dff9b1f48d01a4923f20a0\spring-boot-2.7.18.jar [error] can't determine implemented interfaces of missing type javax.servlet.Filter
when weaving type org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter
when weaving classes
when weaving
when batch building BuildConfig[null] #Files=0 AopXmls=#0
 [Xlint:cantFindType]
(no source information available)
        [Xlint:cantFindType]
C:\Users\raviteja.kothapalli\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot\2.7.18\f6dbdd8da7c2bded63dff9b1f48d01a4923f20a0\spring-boot-2.7.18.jar [error] can't determine implemented interfaces of missing type io.undertow.server.HttpHandler
when weaving type org.springframework.boot.web.embedded.undertow.UndertowWebServer$CloseableHttpHandlerFactory$1
when weaving classes
when weaving
when batch building BuildConfig[null] #Files=0 AopXmls=#0
 [Xlint:cantFindType]
(no source information available)
        [Xlint:cantFindType]
C:\Users\raviteja.kothapalli\.gradle\caches\modules-2\files-2.1\org.springframework.boot\spring-boot\2.7.18\f6dbdd8da7c2bded63dff9b1f48d01a4923f20a0\spring-boot-2.7.18.jar [error] can't determine implemented interfaces of missing type org.springframework.web.server.WebFilter
when weaving type org.springframework.boot.web.reactive.filter.OrderedHiddenHttpMethodFilter
when weaving classes
when weaving
when batch building BuildConfig[null] #Files=0 AopXmls=#0
 [Xlint:cantFindType]
(no source information available)
        [Xlint:cantFindType]

So, what I understood is that I need to include the aspect classes only from specific packages in the inpath in order to overcome the issue, but I can't seem to find the configuration to do it.

Please help me in providing the necessary configuration to include the aspects from external JARs/modules only from specific packages.

build.gradle file in apps module

plugins {
    id 'java'
    id 'java-library'
    id 'io.freefair.aspectj.post-compile-weaving' version "${aspectjVersion}"
}

java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
//    api project(":commons")
    inpath project(":commons")
    api libs.spring.boot.starter
    annotationProcessor libs.lombok
    implementation libs.aspectjrt
}

build.gradle in commons module

plugins {
    id 'java'
    id 'java-library'
    id 'io.freefair.aspectj.post-compile-weaving' version "${aspectjVersion}"
}

java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
    api libs.spring.boot.starter
    annotationProcessor libs.lombok
    implementation libs.aspectjrt
}

settings.gradle in root

rootProject.name = 'multi-module-project'
include('app')
include('commons')

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('spring-boot-ver', '2.7.18')
            library('lombok', 'org.projectlombok:lombok:1.18.30')
            library('spring-boot-starter', 'org.springframework.boot', 'spring-boot-starter').versionRef('spring-boot-ver')
            library('aspectjrt','org.aspectj:aspectjrt:1.9.8')
        }

        testLibs {
            library('junit-jupiter-api','org.junit.jupiter:junit-jupiter-api:5.8.1')
            library('junit-jupiter-engine', 'org.junit.jupiter:junit-jupiter-engine:5.8.1')
        }
    }
}

build.gradle in root

rootProject.ext {
    projectGroup = 'org.example'
    projectVersion = '1.0.0-SNAPSHOT'
    aspectjVersion = '8.4'
}

P.S: Everything is working as expected if all the classes, annotations and aspects are in one single module.


Solution

  • The problem is inpath project(":commons") in your application module. The inpath is meant to be used, if you want to weave aspects into a third-party module. The latter would go on the inpath. But in this case, you simply want to tell the plugin where to find aspects to weave into the current module. The aspect library, however, belongs on the aspectpath.Therefore, you need to change the line to aspect project(":commons"). This is the equivalent to <aspectLibrary/> from my answer to the corresponding Maven question you already found

    I have created a pull request for your project, fixing this problem and improving a few other things. Just go through the commit diffs and comments one by one.

    Now, you can build and run your application. The console log says:

    > Task :app:RunApp.main()
    BasicCommonAnnotation detected
    Running in background
    

    I.e., the aspect gets triggered as expected.