Search code examples
spring-bootaspectjjava-11aspectj-maven-plugin

NoSuchMethodException: com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect.aspectOf()


I have a code that uses AspectJ. I use the compile-time weaving mode. During context initialization, I get an error. Although everything worked before that.

  • annotation

    
    @Retention(RUNTIME)
    @Target(METHOD)
    @Documented
    public @interface AuditAnnotation {
    
        public String value() default "";;
    }
    
    
  • LoggingInterceptorAspect

    @Aspect
    public class LoggingInterceptorAspect {
    
    
        private LoggingService loggingService;
    
        @Autowired
        public LoggingInterceptorAspect(LoggingService loggingService) {
            this.loggingService = loggingService;
        }
    
        @Pointcut("execution(private * *(..))")
        public void privateMethod() {}
    
    @Pointcut("@annotation(com.aspectj.in.spring.boot.aop.aspect.auditlog.annotation.AuditAnnotation)")
        public void annotatedMethodCustom() {}
    
        @Before("annotatedMethodCustom() && privateMethod()")
        public void addCommandDetailsToMessage() throws Throwable {
    
            ZonedDateTime dateTime = ZonedDateTime.now(ZoneOffset.UTC);
            String message = String.format("User controller getUsers method called at %s", dateTime);
    
            System.out.println("+++++++++++++++++++++++++");
            loggingService.log(message);
        }
    }
    
    
  • LoggingInterceptorConfig (It is the error here.)

    @Configuration
    public class LoggingInterceptorConfig {
    
        @Bean
        public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
    
            return Aspects.aspectOf(LoggingInterceptorAspect.class);
        }
    }
    

Here is an error in this line:

  • return Aspects.aspectOf(LoggingInterceptorAspect.class);

  • exception

ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getAutowireCapableLoggingInterceptor' defined in class path resource [com/aspectj/in/spring/boot/aop/aspect/auditlog/interceptor/config/LoggingInterceptorConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect]: Factory method 'getAutowireCapableLoggingInterceptor' threw exception; nested exception is org.aspectj.lang.NoAspectBoundException: Exception while initializing com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect: java.lang.NoSuchMethodException: com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect.aspectOf()

  • pom.xml
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.aspectj.in.spring.boot</groupId>
    <artifactId>aspectj-in-spring-boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>aspectj-in-spring-boot</name>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</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</artifactId>
        </dependency>

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>3.0.3</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.9</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.5</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>com.nickwongdev</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.12.6</version>
                <configuration>
                    <complianceLevel>11</complianceLevel>
                    <source>11</source>
                    <target>11</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <showWeaveInfo>true</showWeaveInfo>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

enter image description here

enter image description here

enter image description here

With the help of reflection, all public methods defined in LoggingInterceptorAspect.class. But why is null returned?

Maybe someone has some ideas why initialization is not happening LoggingInterceptorAspect.class


Solution

  • Thanks for the MCVE on GitHub. Having access to it, helped me to easily identify your problems as follows:

    • Your dynamic @annotation() pointcut is quite broad, targeting all packages. I recommend to additionally add within() in order to limit the aspect scope.
    • In order to auto-inject the logger into the aspect, you want to use a setter instead of the constructor, because AspectJ aspects are expected to have default constructors.
    • Pointcut expression privateMethod() && publicMethod() will never match, because a method cannot be public and private at the same time. You want to use || instead. Or you can simply omit both pointcuts if you want to match both anyway. Also be careful, because in addition to public and private methods there are protected and package-scoped methods too, which you will be excluding if you are only targeting public and private ones.

    Your aspect should look as follows:

    package com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor;
    
    import com.aspectj.in.spring.boot.service.LoggingService;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.time.ZoneOffset;
    import java.time.ZonedDateTime;
    
    @Aspect
    public class LoggingInterceptorAspect {
        private LoggingService loggingService;
    
        @Autowired
        public void setLoggingService(LoggingService loggingService) {
            this.loggingService = loggingService;
        }
    
        @Pointcut("execution(private * *(..))")
        public void privateMethod() {}
    
        @Pointcut("execution(public * *(..))")
        public void publicMethod() {}
    
        @Pointcut("@annotation(com.aspectj.in.spring.boot.aop.aspect.auditlog.annotation.AuditAnnotation)")
        public void annotatedMethodCustom() {}
    
        @Pointcut("within(com.aspectj.in.spring.boot..*)")
        public void applicationScoped() {}
    
    
        @Before("annotatedMethodCustom() && applicationScoped()")
        //@Before("annotatedMethodCustom() && applicationScoped() && (privateMethod() || publicMethod())")
        public void addCommandDetailsToMessage(JoinPoint joinPoint) throws Throwable {
            ZonedDateTime dateTime = ZonedDateTime.now(ZoneOffset.UTC);
            String message = String.format("User controller getUsers method called at %s", dateTime);
            System.out.println("+++ " + joinPoint);
            loggingService.log(message);
        }
    }
    

    Update: I forgot to mention one possible problem in aspects which do not explicitly use execution() because they want to match all methods: When using @annotation() only, you are targetting both call() and execution() joinpoints, as you can see in the compiler log:

    [INFO] Join point 'method-call(java.util.List com.aspectj.in.spring.boot.controller.UserController.getUsersInternal())' in Type 'com.aspectj.in.spring.boot.controller.UserController' (UserController.java:26) advised by before advice from 'com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect' (LoggingInterceptorAspect.java:37)
    [INFO] Join point 'method-execution(java.util.List com.aspectj.in.spring.boot.controller.UserController.getUsersInternal())' in Type 'com.aspectj.in.spring.boot.controller.UserController' (UserController.java:32) advised by before advice from 'com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect' (LoggingInterceptorAspect.java:37)
    CLASSPATH component C:\Program Files\JetBrains\IntelliJ IDEA 2018.3\plugins\maven\lib\maven3\boot\plexus-classworlds.license: java.util.zip.ZipException: zip END header not found
    [INFO] Join point 'method-execution(java.util.List com.aspectj.in.spring.boot.service.impl.DefaultUserService.getMockUsers())' in Type 'com.aspectj.in.spring.boot.service.impl.DefaultUserService' (DefaultUserService.java:34) advised by around advice from 'org.springframework.cache.aspectj.AnnotationCacheAspect' (spring-aspects-5.3.9.jar!AbstractCacheAspect.class:64(from AbstractCacheAspect.aj))
    

    This also leads to duplicate runtime logging output when omitting the && (privateMethod() || publicMethod()) condition:

    +++ call(List com.aspectj.in.spring.boot.controller.UserController.getUsersInternal())
    2021-09-04 16:11:41.213  INFO 17948 --- [o-auto-1-exec-1] sample-spring-aspectj                    : User controller getUsers method called at 2021-09-04T14:11:41.210203700Z
    +++ execution(List com.aspectj.in.spring.boot.controller.UserController.getUsersInternal())
    

    In order to avoid that, you should add a generic execution pointcut:

    @Before("annotatedMethodCustom() && applicationScoped() && execution(* *(..))")