When a filter class annotated with @RefreshScope extends OncePerRequestFilter, it fails to function correctly. The issue arises because the filter is instantiated as a proxy (using CGLIB), causing certain properties of the parent class, such as the logger in GenericFilterBean, to be null.
Reproduction Code:
@RefreshScope
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Filter logic here
filterChain.doFilter(request, response);
}
}
Root Cause: The @RefreshScope annotation creates a CGLIB proxy for the filter bean. The GenericFilterBean class, which is part of the Spring Framework and serves as a base class for OncePerRequestFilter, contains a protected final Logger logger field. This field is initialized directly in the parent class and does not work properly when the bean is proxied. As a result, the logger instance is null, leading to NullPointerException when logging is attempted.
spring-framework version: 6.2.1 spring-boot: 3.4.0 spring-cloud-context: 3.1.7
ERROR
java.lang.NullPointerException: Cannot invoke "org.apache.commons.logging.Log.isDebugEnabled()" because "this.logger" is null
at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:239)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:245)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:102)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:3857)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4462)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1203)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1193)
at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:749)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:772)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1203)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1193)
at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:749)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:203)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:415)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:870)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:437)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:107)
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:516)
Suggested Solution:
to make logger defined in GenericFilterBean
protected final Log logger = LogFactory.getLog(getClass());
static
protected static final Log logger = LogFactory.getLog(getClass());
Comment From: snicoll
Please report that to the Spring Cloud project where they can assess whether adding @RefreshScope
on a filter makes sense.