After debugging, the problem is that mvc configuration class EnableWebMvcConfiguration load too early, servlet not loaded yet.
org.springframework.beans.fact
"After debugging, the problem is that mvc configuration class EnableWebMvcConfiguration load too early, servlet not loaded yet."
I spent a few hours on this. I managed to find a reason why this was happening. My config was split into several files and I was creating a MVC related bean in the Security Config (which was created earlier) forcing to use the MVC config before its time.
The solution was to move the @Bean instance from the security config to the MVC config. I hope it helps other people!
I have the same "No ServletContext set" issue, and i wanted to understand by doing a simple demo, and the demo has helped me. So i am sharing my simple demo in the hope that it helps you too.
Note:
(1) The example i am using is slightly different from the OP but the concept and issue is the same.
(2) I have removed some logs that are not relevant for this demo.
Spring Boot Application simple content - just 3 class:
(1) a main class BeanLoadingDemoApplication
annotated with @SpringBootApplication,
(2) a class WebSecurityConfiguration extends WebSecurityConfigurerAdapter
, which overrides one configure
method
(3) and a couple of test beans, 1 in a stand-alone @Configuration
class, and the other one in the main @SpringBootApplication
class in (2)
The logs after starting the application is as follows below. Just focus on where this part is in the log:
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)
So far ok? Simple, right? Now i add a
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
...
}
in class WebSecurityConfiguration
to configure a 2nd connector for Tomcat. Just focus where this part has "moved":
WebSecurityConfiguration() - Constructor
Can you see that the constructor is called right after starting the application and has been constructed before the root WebApplicationContext
is initialized? By now, the issue is clear.
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
The reason is since Spring Boot
is initializing the embedded Tomcat
before creating its own WebApplicationContext
, it therefore needs to configure the connector i am adding to Tomcat
via the @Bean
before initializing Tomcat and before Root WebApplicationContext is initialized!! But because my @Bean
is in WebSecurityConfiguration class
, that class is getting constructed much earlier than it should have been and it will not have a ServletContext as a result!!
The solution is to move this
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
...
}
in a @Configuration
stand-alone class, so that the constructor of WebSecurityConfiguration
is not called earlier than it should have been, and by doing so, it will get the ServletContext
it needs.
I hope this "visual" example helps.
Cheers