升级 spring boot 2.0.0.RC2 异常 No ServletContext set

Posted

技术标签:

【中文标题】升级 spring boot 2.0.0.RC2 异常 No ServletContext set【英文标题】:ugrade spring boot 2.0.0.RC2 exception No ServletContext set 【发布时间】:2018-08-04 22:21:27 【问题描述】:

经过调试,问题是mvc配置类EnableWebMvcConfiguration加载过早,servlet还没有加载。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:587) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1250) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:502) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:312) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$125/98506158.getObject(Unknown Source) ~[na:na]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388) [spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234) [spring-boot-2.0.0.RC2.jar:2.0.0.RC2]
    at com.manway.BccApplication.main(BccApplication.java:16) [main/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.0.0.RC2.jar:2.0.0.RC2]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:579) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 24 common frames omitted
Caused by: java.lang.IllegalStateException: No ServletContext set
    at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:485) ~[spring-webmvc-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5de64506.CGLIB$resourceHandlerMapping$40(<generated>) ~[spring-boot-autoconfigure-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5de64506$$FastClassBySpringCGLIB$$edc706a5.invoke(<generated>) ~[spring-boot-autoconfigure-2.0.0.RC2.jar:2.0.0.RC2]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:361) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$5de64506.resourceHandlerMapping(<generated>) ~[spring-boot-autoconfigure-2.0.0.RC2.jar:2.0.0.RC2]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 25 common frames omitted

【问题讨论】:

【参考方案1】:

“经过调试,问题是mvc配置类EnableWebMvcConfiguration加载过早,servlet还没有加载。”

我在这上面花了几个小时。 我设法找到了发生这种情况的原因。 我的配置被拆分成几个文件,我在 Security Config(之前创建的)中创建了一个与 MVC 相关的 bean,从而强制在其时间之前使用 MVC 配置。

解决方案是将 @Bean 实例从安全配置移动到 MVC 配置。 我希望它可以帮助其他人!

【讨论】:

在我的例子中,它是MessageSource bean,它已在WebMvcConfigurer 配置类中定义,同时被注入使用messageSource() 方法在同一类中创建其他一些bean打电话。当我删除方法调用注入并使用@AutowiredmessageSource 注入同一个类时,这个奇怪的错误变得更加明显:“创建名称为'messageSource'的bean时出错:当前正在创建请求的bean:是否存在无法解析的循环引用?”。所以我将MessageSource bean 提取到另一个配置类中,它可以工作 也帮助了我!谢谢 说真的,为我节省了您花费的时间...感谢您的辛勤工作!【参考方案2】:

我有同样的“没有设置 ServletContext”的问题,我想通过做一个简单的演示来理解,这个演示帮助了我。所以我分享了我的简单演示,希望它也能对你有所帮助。 注意: (1) 我使用的示例与 OP 略有不同,但概念和问题是相同的。 (2) 我已经删除了一些与本次演示无关的日志。

Spring Boot Application 简单内容——只有 3 个类: (1)一个主类BeanLoadingDemoApplication用@SpringBootApplication注解, (2) 一个class WebSecurityConfiguration extends WebSecurityConfigurerAdapter,它覆盖了一个configure 方法 (3) 和几个测试 bean,一个在独立的 @Configuration 类中,另一个在 (2) 中的主 @SpringBootApplication 类中

启动应用程序后的日志如下。只需关注这部分在日志中的位置

INFO  o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 700 ms
INFO  c.example.demo.WebSecurityConfiguration - WebSecurityConfiguration() - Constructor
INFO  c.e.demo.BeanLoadingDemoApplication - Starting BeanLoadingDemoApplication 
INFO  o.s.b.w.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
INFO  o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 700 ms
INFO  c.example.demo.WebSecurityConfiguration - WebSecurityConfiguration() - Constructor
INFO  com.example.demo.TestBeanConfiguration - #############################
INFO  com.example.demo.TestBeanConfiguration - @Configuration - @Bean - testBean()
INFO  com.example.demo.TestBeanConfiguration - #############################
INFO  c.e.demo.BeanLoadingDemoApplication - #############################
INFO  c.e.demo.BeanLoadingDemoApplication - @SringBootApp - @Bean - testBean2
INFO  c.e.demo.BeanLoadingDemoApplication - #############################
INFO  c.example.demo.WebSecurityConfiguration - =======================================
INFO  c.example.demo.WebSecurityConfiguration - @Configuration - WebSecurityConfiguration - @Override configure
INFO  c.example.demo.WebSecurityConfiguration - =======================================
INFO  c.e.demo.BeanLoadingDemoApplication - Started BeanLoadingDemoApplication in 1.371 seconds (JVM running for 2.326)

到目前为止还好吗?很简单,对吧?现在我添加一个

@Bean
public ServletWebServerFactory servletContainer()  
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
...

WebSecurityConfiguration 类中为 Tomcat 配置第二个连接器。只需关注该部分“移动”的位置

WebSecurityConfiguration() - 构造函数

你能看到构造函数是在启动应用程序后立即调用的,并且是在根WebApplicationContext 初始化之前构造的吗?至此,问题已经很清楚了。

INFO  c.e.demo.BeanLoadingDemoApplication - Starting BeanLoadingDemoApplication
INFO  c.example.demo.WebSecurityConfiguration - WebSecurityConfiguration() - Constructor
INFO  o.s.b.w.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http) 8080 (http)
INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
INFO  o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 785 ms
INFO  com.example.demo.TestBeanConfiguration - #############################
INFO  com.example.demo.TestBeanConfiguration - @Configuration - @Bean - testBean()
INFO  com.example.demo.TestBeanConfiguration - #############################
INFO  c.e.demo.BeanLoadingDemoApplication - #############################
INFO  c.e.demo.BeanLoadingDemoApplication - @SringBootApp - @Bean - testBean2
INFO  c.e.demo.BeanLoadingDemoApplication - #############################
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource
Caused by: java.lang.IllegalStateException: No ServletContext set

原因是因为Spring Boot 在创建自己的WebApplicationContext 之前初始化嵌入的Tomcat,因此它需要配置我正在添加到的连接器Tomcat 通过@Bean 在初始化 Tomcat 之前和在初始化 Root WebApplicationContext 之前!!但是因为我的 @BeanWebSecurityConfiguration class 中,所以该类的构建时间比它应该的要早得多结果它不会有 ServletContext!!

解决方法是移动这个

@Bean
public ServletWebServerFactory servletContainer() 
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    ...

在一个@Configuration 独立类中,这样WebSecurityConfiguration 的构造函数就不会早于它应该被调用的时间,通过这样做,它将获得它需要的ServletContext

我希望这个“视觉”示例有所帮助。

干杯

【讨论】:

详细示例!谢谢

以上是关于升级 spring boot 2.0.0.RC2 异常 No ServletContext set的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 2.0干货系列:Spring Boot1.5X升级到2.0指南

Spring Boot版本升级——mysql报错

升级 Spring Boot

将 Spring Boot 升级到 2.4.1

从 Spring Boot 1.5 升级时为 Spring Boot 2.0 acuator 框架配置安全性

从早期 Spring Boot 版本升级