NPE 在不同端口上启动执行器时

NPE when starting actuator on distinct port

将执行器配置为在不同的端口上启动后,应用程序失败并显示以下堆栈跟踪:

java.lang.NullPointerException: null
at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:241) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
  at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:270) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
  at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:106) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
  at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4528) [tomcat-embed-core-9.0.37.jar:9.0.37]

aop 发出以下信息警告:

2020-08-16 10:01:11.240  INFO 83848 --- [           main] o.s.aop.framework.CglibAopProxy          : Unable to proxy interface-implementing method [public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig) throws javax.servlet.ServletException] because it is marked as final: Consider using interface-based JDK proxies instead!

这是我的 gradle 依赖项:

plugins {
  id 'org.springframework.boot' version '2.3.3.RELEASE'
...
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.5.RELEASE'
implementation 'org.springframework.security:spring-security-jwt:1.0.10.RELEASE'

我们确实有一个 @Aspect 注释 class:

...
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Component;
...
@Aspect
@Component
@Slf4j
public class MyFilter {

    @Around("execution(public void org.springframework.security.web.FilterChainProxy.doFilter(..))")
    public void handleRequestRejectedException (ProceedingJoinPoint pjp) throws Throwable {
        try {
            pjp.proceed();
        } catch (RequestRejectedException exception) {
           ...
        }
    }
}

我发现了一些讨论这个问题的问题,与aop有关,但我还没有找到解决方案。

any/all 帮助感谢。

OP 用一些示例代码更新了问题后,我发现问题不是切入点太笼统,而是捕获了太多 classes,就像我在我的链接中提到的另一个问题一样评论但其他内容:

James,您的目标是方法 FilterChainProxy.doFilter(..),即直接 Spring 框架 class。问题是,正如您在日志中收到的错误消息中也可以看到的那样:

o.s.aop.framework.CglibAopProxy:
  Unable to proxy interface-implementing method [
    public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig)
    throws javax.servlet.ServletException
  ]
  because it is marked as final:
  Consider using interface-based JDK proxies instead!

问题是目标 class 派生自 GenericFilterBean,其中 init(..) 方法被标记为 final。 CGLIB 通过 subclassing 和覆盖 non-private 方法工作,但不能覆盖 final 方法。因此,Spring AOP 抱怨。

该错误消息还提示您找到问题的解决方案:您可以使用通过实现接口工作的 JDK 代理,而不是对目标使用 CGLIB 代理。幸运的是,doFilter 是一个接口方法,更准确地说是 JavaEE 方法 Filter.doFilter(..).

的实现

现在下一个问题是您使用的不是普通版 Spring,而是 Spring Boot,它以组合预设而闻名,似乎无法切换到 JDK 代理,即使在普通 Spring 中这甚至是默认值。但是 Boot 想变得太 user-friendly 和聪明,让你陷入这个陷阱。我不记得处理这个问题的 Spring 引导票,但我上次检查它是未解决的。

避免整个情况的一种方法是使用完整的 AspectJ 而不是只为您的方面使用 Spring AOP,从而使您摆脱 proxy-based “AOP lite” 方法并使您能够直接修改目标 class 或者通过 call() 切入点(不支持 Spring AOP).

另一种解决方案是使用您的方面连接到另一个问题较少的目标 class/method,而不是直接连接到具有最终方法的 Spring 框架 class。

P.S.: 我不是 Spring 用户,我只是碰巧从这里回答 AOP-related 问题知道了一些细节。实际上,令我惊讶的是 Spring 实现的 CGLIB 代理对 final 方法并不宽容,只是忽略它们并记录警告,而不是试图覆盖它们并产生错误。也许有一些配置选项,但我让 Spring 的人来回答这个问题,我在这里一无所知。

我无法回答得更准确,因为我需要查看完整的 MCVE,最好是 GitHub 上的 Maven 或 Gradle 项目,以便进一步分析您的情况。我只是想在这里解释一些基础知识。