Skip to content

Spring MVC: @EnableWebMvc should be redeclared in each ApplicationContext #34863

Closed as not planned
@smfukaya

Description

@smfukaya

Hi,

I'm not sure if this is a Spring Boot problem (I'm using spring-boot-3.4.2 with spring-webmvc-6.2.2), a problem with @EnableWebMvc Javadoc, or an issue with my implementation.

Similar to 34843, it seems that I must redeclare @EnableWebMvc in each ServletRegistrationBean configuration classes, contrary to what the annotation Javadoc says that should only one configuration class should have that annotation.

* <p><strong>Note:</strong> only one {@code @configuration} class may have the
* {@code @EnableWebMvc} annotation to import the Spring Web MVC
* configuration. There can however be multiple {@code @configuration} classes
* implementing {@code WebMvcConfigurer} in order to customize the provided
* configuration.

In my implementation, I had disabled the Spring Boot DispatcherServletAutoConfiguration (MainApplication.java), and created a configuration class (MvcConfig.java) annotated with @EnableWebMvc and extending WebMvcConfigurer where I create two ServletRegistrationBean (api and webapi) using their corresponding configuration classes (ApiConfig and WebAppConfig).

If I redeclare @EnableWebMvc in the ServletRegistrationBean specific configuration class (e.g. ApiConfig), I can call their scanned controllers with no problem. But if I don't do that (e.g. WebAppConfig), when I call any scanned controller of that servlet I get a HTTP 404 error with a message "No endpoint GET /my-app-context/webapi/my-request-mapping-name."

MainApplication.java:

@Configuration
@SpringBootApplication(
		exclude = {
				DispatcherServletAutoConfiguration.class,
				ErrorMvcAutoConfiguration.class,
				SecurityAutoConfiguration.class,
				DataSourceAutoConfiguration.class,
				DataSourceTransactionManagerAutoConfiguration.class,
				HibernateJpaAutoConfiguration.class
				}
		)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MainApplication extends SpringBootServletInitializer {

}

MvcConfig.java:

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

	private static final String DISPATCHER_SERVLET_MAPPINGS_FORMAT = "/%s/*";

	@Bean
	DispatcherServletPath dispatcherServletPath() {
		return () -> "/";
	}

	/**
	 * Servlet for Web Application.
	 * @return
	 */
	@Bean
	ServletRegistrationBean<DispatcherServlet> app() {
		DispatcherServlet dispatcherServlet = new DispatcherServlet();
		AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
		applicationContext.register(WebAppConfig.class);
		dispatcherServlet.setApplicationContext(applicationContext);
		ServletRegistrationBean<DispatcherServlet> servletRegistrationBean =
        		new ServletRegistrationBean<>(
        				dispatcherServlet,
        				String.format(DISPATCHER_SERVLET_MAPPINGS_FORMAT, WebAppConfig.WEB_SERVLET_NAME
        						)
        				);
		servletRegistrationBean.setName(WebAppConfig.WEB_SERVLET_NAME);
		return servletRegistrationBean;
	}

	/**
	 * Servlet for API consumed by external systems.
	 * @return
	 */
	@Bean
	ServletRegistrationBean<DispatcherServlet> api() {
		DispatcherServlet dispatcherServlet = new DispatcherServlet();
		AnnotationConfigWebApplicationContext applicationContext =
        		new AnnotationConfigWebApplicationContext();
		applicationContext.register(ApiConfig.class);
		dispatcherServlet.setApplicationContext(applicationContext);
		ServletRegistrationBean<DispatcherServlet> servletRegistrationBean =
        		new ServletRegistrationBean<>(
        				dispatcherServlet,
        				String.format(DISPATCHER_SERVLET_MAPPINGS_FORMAT, ApiConfig.API_SERVLET_NAME
        						)
        				);
		servletRegistrationBean.setName(ApiConfig.API_SERVLET_NAME);
		return servletRegistrationBean;
	}
}

ApiConfig.java (WITH @EnableWebMvc):

@Configuration
@EnableWebMvc

@EnableAsync
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableScheduling
@ComponentScan(basePackages = {
		"my.packages.api"
		}
		, includeFilters = @Filter(Controller.class))
public class ApiConfig {
	/**
	 * Servlet name.
	 */
	public static final String API_SERVLET_NAME = "api";

	public ApiConfig() {  }
}

WebAppConfig.java (WITHOUT @EnableWebMvc):

@Configuration

@EnableAsync
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableScheduling
@ComponentScan(basePackages = {
		"my.packages.web"
		}
		, includeFilters = @Filter(Controller.class))
public class WebAppConfig {
	/**
	 * servlet name.
	 */
	public static final String WEB_SERVLET_NAME = "webapi";

	public WebAppConfig() { }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: webIssues in web modules (web, webmvc, webflux, websocket)status: declinedA suggestion or change that we don't feel we should currently apply

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions