I am facing two issues when I'm using DelegatingWebMvcConfiguration in my custom autoconfiguration module:
-
Creating a configuration class that simply extends
DelegatingWebMvcConfiguration
does not work, because methods likeextendHandlerExceptionResolvers
are not called. The same method is called, whenWebMvcConfigurer
is implemented instead. Why is this the case? -
As a workaround, I wrote the following configuration class:
@Configuration
@EnableWebMvc
public class ExampleConfiguration extends DelegatingWebMvcConfiguration {
@Configuration
public class Inner implements WebMvcConfigurer {
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
//Calls methods like ExampleConfiguration.this.mvcContentNegotiationManager() that are unavailable in WebMvcConfigurer
}
}
@Bean
public Dummy dummy() {
return new Dummy();
}
protected static final class Dummy {
}
}
If the Dummy
bean does not exist (or is unused), I'm getting the following error:
The dependencies of some of the beans in the application context form a cycle:
documentationPluginsBootstrapper defined in URL [jar:file:/.../maven/repository/io/springfox/springfox-spring-web/2.9.2/springfox-spring-web-2.9.2.jar!/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.class]
↓
webMvcRequestHandlerProvider defined in URL [jar:file:/.../maven/repository/io/springfox/springfox-spring-web/2.9.2/springfox-spring-web-2.9.2.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]
↓
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
┌─────┐
| com.example.configuration.ExampleConfiguration $Inner
↑ ↓
| com.example.configuration.ExampleConfiguration
└─────┘
Both of these things look like a bug to me, but I am not experienced enough with Spring to be sure.
Comment From: wilkinsona
Thanks for the report. It's extendHandlerExceptionResolvers
on DelegatingWebMvcConfiguration
that calls the corresponding method on the configurers. If the configurers are being called then extendHandlerExceptionResolvers
must be getting called on an instance of DelegatingWebMvcConfiguration
.
I suspect that you may have two instances of DelegatingWebMvcConfiguration
. The presence of org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
in the error suggests that this is the case. This could be due to auto-configuration ordering. I think it would happen if Boot's WebMvcAutoConfiguration
runs before your custom auto-configuration.
That's about all that I can infer from what you have shared thus far. If the above doesn't help and you would like us to spend some more time investigating, please take the time to provide a complete and minimal example that reproduces the problem.
Comment From: Jobarion
I've created a minimal example: - Autoconfiguration: https://github.com/Jobarion/bug-demo-spring-autoconfiguration - Application: https://github.com/Jobarion/bug-demo-spring
Another observation I've had is that the name of the configuration class matters. Maybe it's some local quirk, but if I rename class, I always get the cycle error
Comment From: wilkinsona
Thanks for the sample. When I run it I can see that method1 is not being called. I have also been able to confirm some of my guesses.
There are indeed two DelegatingWebMvcConfiguration
beans. I modified the main method to look like the following:
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
Map<String, DelegatingWebMvcConfiguration> mvcConfigurations = context.getBeansOfType(DelegatingWebMvcConfiguration.class);
System.out.println(mvcConfigurations);
}
And it output the following:
{org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration=org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration@2b9b7f1f, com.example.configuration.MvcConfiguration=com.example.configuration.MvcConfiguration$$EnhancerBySpringCGLIB$$c0c4c9ef@264c5d07}
There's no ordering on com.example.configuration.MvcConfiguration
so it's running after WebMvcAutoConfiguration
and duplicating some of the beans that it creates. If you want to replace Spring Boot's MVC auto-configuration with your own, you should use @AutoConfigureBefore(WebMvcAutoConfiguration.class)
on MvcConfiguration
. With this change in place, I then see a failure:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| com.example.configuration.MvcConfiguration$Inner
↑ ↓
| com.example.configuration.MvcConfiguration
└─────┘
This circular dependency exists because MvcConfiguration
requires all WebMvcConfigurer
beans when it is being created and, because it's not static
, Inner
requires an MvcConfiguration
instance to create it so there's a cycle. I think Inner
was an attempt at working around the problem so it can probably just be removed. If I do that combined with the ordering change above, method2 is then called as expected.
Another observation I've had is that the name of the configuration class matters. Maybe it's some local quirk, but if I rename class, I always get the cycle error
In the absence of any ordering on an auto-configuration class, alphabetical ordering is used. This ensures that the order remains stable and isn't affected by changes to the ordering of the classpath and the like.