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()
<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>
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
Thanks for the MCVE on GitHub. Having access to it, helped me to easily identify your problems as follows:
@annotation()
pointcut is quite broad, targeting all packages. I recommend to additionally add within()
in order to limit the aspect scope.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(* *(..))")