NoSuchMethodException:com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect.aspectOf()
NoSuchMethodException: com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect.aspectOf()
我有一个使用 AspectJ 的代码。我使用编译时编织模式。在上下文初始化期间,出现错误。尽管在那之前一切正常。
注解
@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(这里报错。)
@Configuration
public class LoggingInterceptorConfig {
@Bean
public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
return Aspects.aspectOf(LoggingInterceptorAspect.class);
}
}
这一行有一个错误:
return Aspects.aspectOf(LoggingInterceptorAspect.class);
异常
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>
在反射的帮助下,在LoggingInterceptorAspect.class中定义了所有public方法。但为什么 null returned?
也许有人知道为什么没有进行初始化LoggingInterceptorAspect.class
@Aspect
@Component
public class LoggingInterceptorAspect {
使用@Component
,您在应用程序上下文中注册了一个 LoggingInterceptorAspect 类型的 bean。
@Bean
public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
这里 @Bean
您在应用程序上下文中再次注册了另一个类型为 LoggingInterceptorAspect 的 bean
当两个都是单例时,为什么要注册 2 个相同类型的 bean?
感谢 GitHub 上的 MCVE。访问它后,帮助我轻松识别您的问题如下:
- 您的动态
@annotation()
切入点非常广泛,针对所有包。我建议另外添加 within()
以限制方面范围。
- 为了将记录器自动注入方面,您希望使用 setter 而不是构造函数,因为 AspectJ 方面应该具有默认构造函数。
- 切入点表达式
privateMethod() && publicMethod()
永远不会匹配,因为一个方法不能同时是 public 和私有的。您想改用 ||
。或者,如果您无论如何都想匹配两个切入点,则可以简单地省略两个切入点。还要小心,因为除了 public 和私有方法之外,还有受保护和包范围的方法,如果你只针对 public 和私有方法,你将被排除在外。
您的外观应该如下所示:
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);
}
}
更新: 我忘了在未明确使用 execution()
的方面提及一个可能的问题,因为它们想匹配所有方法:使用 [=14= 时] 只有,你的目标是 call()
和 execution()
连接点,正如你在编译器日志中看到的那样:
[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))
当省略 && (privateMethod() || publicMethod())
条件时,这也会导致重复的运行时日志输出:
+++ 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())
为了避免这种情况,您应该添加一个通用的执行切入点:
@Before("annotatedMethodCustom() && applicationScoped() && execution(* *(..))")
我有一个使用 AspectJ 的代码。我使用编译时编织模式。在上下文初始化期间,出现错误。尽管在那之前一切正常。
注解
@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(这里报错。)
@Configuration public class LoggingInterceptorConfig { @Bean public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() { return Aspects.aspectOf(LoggingInterceptorAspect.class); } }
这一行有一个错误:
return Aspects.aspectOf(LoggingInterceptorAspect.class);
异常
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>
在反射的帮助下,在LoggingInterceptorAspect.class中定义了所有public方法。但为什么 null returned?
也许有人知道为什么没有进行初始化LoggingInterceptorAspect.class
@Aspect
@Component
public class LoggingInterceptorAspect {
使用@Component
,您在应用程序上下文中注册了一个 LoggingInterceptorAspect 类型的 bean。
@Bean
public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
这里 @Bean
您在应用程序上下文中再次注册了另一个类型为 LoggingInterceptorAspect 的 bean
当两个都是单例时,为什么要注册 2 个相同类型的 bean?
感谢 GitHub 上的 MCVE。访问它后,帮助我轻松识别您的问题如下:
- 您的动态
@annotation()
切入点非常广泛,针对所有包。我建议另外添加within()
以限制方面范围。 - 为了将记录器自动注入方面,您希望使用 setter 而不是构造函数,因为 AspectJ 方面应该具有默认构造函数。
- 切入点表达式
privateMethod() && publicMethod()
永远不会匹配,因为一个方法不能同时是 public 和私有的。您想改用||
。或者,如果您无论如何都想匹配两个切入点,则可以简单地省略两个切入点。还要小心,因为除了 public 和私有方法之外,还有受保护和包范围的方法,如果你只针对 public 和私有方法,你将被排除在外。
您的外观应该如下所示:
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);
}
}
更新: 我忘了在未明确使用 execution()
的方面提及一个可能的问题,因为它们想匹配所有方法:使用 [=14= 时] 只有,你的目标是 call()
和 execution()
连接点,正如你在编译器日志中看到的那样:
[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))
当省略 && (privateMethod() || publicMethod())
条件时,这也会导致重复的运行时日志输出:
+++ 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())
为了避免这种情况,您应该添加一个通用的执行切入点:
@Before("annotatedMethodCustom() && applicationScoped() && execution(* *(..))")