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 的代码。我使用编译时编织模式。在上下文初始化期间,出现错误。尽管在那之前一切正常。

这一行有一个错误:

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>

在反射的帮助下,在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(* *(..))")