Search code examples
springspring-bootspring-mvc

ConditionalOnMissingBean still create a InternalResourceViewResolver


I have some spring boot (using 2.7.18) autoconfiguration, which import this configuration, that creates an InternalResourceViewResolver

@Configuration
@ConditionalOnClass(javax.servlet.jsp.jstl.core.Config.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ViewResolverConfiguration {

@Bean
public InternalResourceViewResolver internalResourceViewResolver(
        @Value("${spring.mvc.view.prefix:/WEB-INF/jsp/}") String prefix,
        @Value("${spring.mvc.view.suffix:.jsp}") String suffix) {
    final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setPrefix(prefix);
    viewResolver.setSuffix(suffix);
    viewResolver.setOrder(InternalResourceViewResolver.HIGHEST_PRECEDENCE);
    return viewResolver;
}

}

I believe that to be normal conditional configuration. However, at runtime I had two InternalResourceViewResolvers, as seen from /actuator/beans:

"internalResourceViewResolver": {
                "aliases": [],
                "scope": "singleton",
                "type": "org.springframework.web.servlet.view.InternalResourceViewResolver",
                "resource": "class path resource [com/example/ViewResolverConfiguration.class]",
                "dependencies": [
                    "com.example.ViewResolverConfiguration"
                ]
            },
"defaultViewResolver": {
                "aliases": [],
                "scope": "singleton",
                "type": "org.springframework.web.servlet.view.InternalResourceViewResolver",
                "resource": "class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]",
                "dependencies": [
                    "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter"
                ]
            },

As can be seen, it is the one from WebMvcAutoConfiguration, which for some reason ignores a @ConditionalOnMissingBean condition.

    @Bean
    @ConditionalOnMissingBean
    public InternalResourceViewResolver defaultViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix(this.mvcProperties.getView().getPrefix());
        resolver.setSuffix(this.mvcProperties.getView().getSuffix());
        return resolver;
    }

I can see the positive evaluation from /actuator/conditions, that it does configure it in on that condition:

 "WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#defaultViewResolver": [
                {
                    "condition": "OnBeanCondition",
                    "message": "@ConditionalOnMissingBean (types: org.springframework.web.servlet.view.InternalResourceViewResolver; SearchStrategy: all) did not find any beans"
                }
            ],

Aside from the problem of them being mostly identical, it shouldn't configure the defaultViewResolver, or is there something I don't understand? Has onyone any clues how to investigate or resolve the situation


Solution

  • WebMvcAutoConfiguration autoconfigure in a different priority than the default one with @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10). Therefore when @ConditionalOnMissingBean is evaluated, your bean is not yet created and therefore it creates the default one.

    To fix it, you can either put @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) or (as you suggested) @AutoConfigureBefore(org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.class) on your @Configuration class.

    If you want to see in which order bean are created, the only way I see is to put a breakpoint in this method (AutoConfigurationSorter), which seems responsible of the ordering. debug: true configuration does not seems to print the evaluation report in the order which beans has been created.