SpringBoot从2.7.2升级到3.2.0后Filter抛出非空异常排查
Filter must not be null异常解决 升级SpringBoot版本从2.7.2到3.2.0后,启动嵌入式Tomcat时出现以下异常:
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)
at
org.springframework.boot.web.embedded.tomcat.TomcatWebServer.(TomcatWebServer.java:104)
at
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:501)
at
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:218)
at
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:188)
at
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162)
... 11 common frames omitted Caused by:
java.lang.IllegalArgumentException: Filter must not be null at
org.springframework.util.Assert.notNull(Assert.java:172) at
org.springframework.boot.web.servlet.AbstractFilterRegistrationBean.getDescription(AbstractFilterRegistrationBean.java:223)
at
org.springframework.boot.web.servlet.RegistrationBean.onStartup(RegistrationBean.java:47)
at
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:241)
at
org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)
at
org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4850)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)
at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332)
at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
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:866)
at
org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:845)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)
at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332)
at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
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:866)
at
org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:240)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)
at
org.apache.catalina.core.StandardService.startInternal(StandardService.java:433)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)
at
org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:917)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:488) at
org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123)
... 16 common frames omitted
涉及的配置类代码如下:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Value("${system.debug.enabled:false}") private boolean debugEnabled; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(httpHeaderInterceptor()); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/log/**").allowedOrigins("*") .allowedMethods("POST", "GET", "PUT", "PATCH", "OPTIONS", "DELETE") .allowedHeaders("header1", "header2", "header3") .allowedHeaders("Origin", "Authorization", "X-Requested-With", "Content-Type", "Accept") .allowCredentials(false).maxAge(3600); } @Bean public HandlerInterceptor httpHeaderInterceptor() { return (HandlerInterceptor) new HttpHeaderInterceptor(); } private FilterRegistrationBean newFilterRegistrationBean(Filter filter, int order) { final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setOrder(order); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; } @Bean public FilterRegistrationBean stopWatchFilter() { Filter filter = debugEnabled ? new StopWatchFilter() : new NoopFilter(); FilterRegistrationBean filterRegistrationBean = newFilterRegistrationBean(filter, 1); return filterRegistrationBean; } @Bean public FilterRegistrationBean gzipFilter() { GZipRequestFilter gzipRequestFilter = new GZipRequestFilter(); return newFilterRegistrationBean(gzipRequestFilter, 2); } @Bean public FilterRegistrationBean registerOpenSessionInViewFilterBean() { OpenEntityManagerInViewFilter openEntityManagerInViewFilter = new OpenEntityManagerInViewFilter(); return newFilterRegistrationBean(openEntityManagerInViewFilter, 1); } }
经排查,异常由gzipFilter和registerOpenSessionInViewFilterBean这两个Filter注册方法引发——虽然初始化了Filter实例,但启动时仍提示Filter为null,移除该部分代码后服务器可正常启动。
问题原因
核心问题出在newFilterRegistrationBean方法中:该方法接收了Filter实例,但没有将Filter赋值给FilterRegistrationBean。SpringBoot 3.x版本对FilterRegistrationBean的校验逻辑比2.7.x更严格,启动时会直接断言Filter不能为null,而未赋值的FilterRegistrationBean内部Filter属性默认是null,因此触发异常。
解决方案
修改newFilterRegistrationBean方法,将传入的Filter实例设置到FilterRegistrationBean中,有两种实现方式:
方式一:使用带Filter参数的构造器
private FilterRegistrationBean newFilterRegistrationBean(Filter filter, int order) { final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter); filterRegistrationBean.setOrder(order); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; }
方式二:调用setFilter方法赋值
private FilterRegistrationBean newFilterRegistrationBean(Filter filter, int order) { final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(filter); // 新增该行,设置Filter实例 filterRegistrationBean.setOrder(order); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; }
修改后重新启动,异常即可解决。
内容的提问来源于stack exchange,提问作者Subodh Joshi

