Spring Boot Web(Gradle 配置)在我添加 AOP/Advice 时中断

Spring Boot Web (Gradle config) breaks when I add AOP/Advice

我不确定这是一个错误配置,我只是不知道自从 AOP 教程以来我发现所有使用 Maven 的部分,但我使用 Gradle.

所以我使用 start.spring.io 创建的项目(Kotlin 1.5.21,Gradle 7.1.1,Spring Boot 2.5.3)和 spring-boot-starter-web.

plugins {
    id("org.springframework.boot") version "2.5.3"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    kotlin("jvm") version "1.5.21"
    kotlin("plugin.spring") version "1.5.21"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
}

然后我添加了

implementation("org.springframework.boot:spring-boot-starter-aop")

和一个建议

@Target(AnnotationTarget.FUNCTION) @MustBeDocumented
annotation class UserAuthorized(val roles: Array<String>)

@Component @Aspect
class UserAuthorizedAdvice {
    @Pointcut("@target(com.example.springbootplayground2.auth.UserAuthorized)")
    fun userAuthorizedMethods() {}

    @Before("userAuthorizedMethods()")
    fun checkIfUserHasRequiredRole(jp: JoinPoint) {...}
}

现在应用程序在启动时崩溃(最后出现错误消息)。我试图通过添加 @EnableAspectJAutoProxy 注释来修复它,但没有帮助。

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)
class SpringbootPlayground2Application

在 Linux 上崩溃

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tomcatServletWebServerFactory' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedTomcat.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'errorPageCustomizer' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'errorPageCustomizer' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dispatcherServletRegistration' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean]: Factory method 'dispatcherServletRegistration' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'multipartConfigElement' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Unexpected AOP exception; nested exception is java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "cause" is null

引起
Caused by: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "cause" is null
    at org.springframework.cglib.core.CodeGenerationException.<init>(CodeGenerationException.java:25) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:587) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.apply(AbstractClassGenerator.java:110) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.apply(AbstractClassGenerator.java:108) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.internal.LoadingCache.call(LoadingCache.java:54) ~[spring-core-5.3.9.jar:5.3.9]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
    at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:419) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:57) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:206) ~[spring-aop-5.3.9.jar:5.3.9]

在 MacOs (M1-cpu) 上,此崩溃 java.lang.NullPointerException: null 是由

引起的
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
                at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:450) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:199) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory$$FastClassBySpringCGLIB$c83fa9f.invoke(<generated>) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:180) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.9.jar:5.3.9]
                at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory$$EnhancerBySpringCGLIB$$f87b4803.getWebServer(<generated>) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160) ~[spring-boot-2.5.3.jar:2.5.3]
                ... 8 common frames omitted
Caused by: java.lang.IllegalStateException: StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start
                at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.rethrowDeferredStartupExceptions(TomcatWebServer.java:187) ~[spring-boot-2.5.3.jar:2.5.3]
                at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:126) ~[spring-boot-2.5.3.jar:2.5.3]
                ... 25 common frames omitted

更新

在这里你可以找到 minimal version of my project

更新2

该项目现在有一个 solution branch 合并了 kriegaex 的修复程序。

实际上,我太专注于帮助您解决范围界定问题,然后考虑您对未被拾取的方面的描述,以至于我忽略了显而易见的事情:

您只是使用了错误的切入点类型。 @target() 用于类型注释,而您使用需要 @annotation() 切入点的方法注释:

@Pointcut("@annotation(com.example.springbootplayground2.UserAuthorized)")
fun userAuthorizedMethods() {}

现在你的方面开始了,因为 @annotation() 可以静态确定,你甚至不需要 && within(...)。但添加它作为保障总是一个好主意。

为您吸取的教训:始终尝试提供所有相关代码,如果您不确定错误的位置,请始终 post 和 MCVE。你没有 post 你的带有注释方法的控制器,否则我可能马上就注意到了。现在我只有在克隆您的存储库并等待 Gradle 执行其占用带宽的下载操作后才有机会注意到故障。