The desired steps to a valid Formatters registration are:

  1. First of all, the method addWebMvcConfigurers located in WebMvcConfigurationComposite should registrate all the web configurers. That includes the formatters.
  2. After that, when some component needs to @Autowired a ConversionService instance, the method mvcConversionService located in WebMvcConfigurationSupport obtains a ConversionService instance and register all the existing formatters on it before return the instance.

However, if a @Configuration class extends the WebSecurityConfigurerAdapter abstract class, some component is trying to @Autowired a ConversionService instance before the formatters have been registered in the Spring context, so the addFormatters method doesn't include any formatters on it.

I've just created the following proof of concept that uses Spring Boot to reproduce this issue:

https://github.com/jcagarcia/proofs/tree/master/spring-security-and-formatters

If you execute this proof of concept using the mvn compile spring-boot:run command, you could check that the Create Pets view shows an enum value without apply the conversion to String.

Seems like the component that requires the ConversionService instance is the ContentNegotiation that is beeing @Autowired in the WebSecurityConfigurerAdapter.

A simple work-around is to @Override the setContentNegotiationStrategy method in our SecurityConfiguration class without include the @Autowired annotation. (The proof of concept includes this work-around commented)

After that, execute this proof of concept again using the mvn compile spring-boot:run command and you could check that the Create Pets view shows an enum value with a valid format applied.

I think this is not the best solution because the ContentNegotiationStrategy is not beeing @Autowired anymore.

I've just created a question in StackOverflow to obtain some answer:

http://stackoverflow.com/questions/42086046/spring-formatters-have-been-not-registered-when-some-class-extends-websecurityco

Best Regards,

Comment From: jcagarcia

Also, I've just created a new issue in the Spring Framework JIRA

Check out https://jira.spring.io/browse/SPR-15239

Comment From: franrevoredo

I've had the same problem, this bug prevented the select fields from my html5 views from beign populated with select2.

Comment From: cordin

Any chance to take a look at this issue? It happens in the projects generated with Roo 2.0 RC1 and it would be nice to be able to solve it before the final release, without using the @jcagarcia workaround.

Comment From: rwinch

Thanks for the ping.

Everything I see points to an ordering issue with Spring Boot. I don't think there is anything Spring Security can do. Spring Security needs the ContentNegotiationStrategy so we cannot disable the lookup.

As Rossen alludes to on the Spring Framework ticket, I think Boot needs to change some ordering of the configuration. Specifically, it may be valuable to break out the ContentNegotiationStrategy to load earlier.

@wilkinsona What are your thoughts?

Comment From: wilkinsona

It's been a while, but my recollection of the issue is that it could easily be triggered by a user's own configuration as well as by the current arrangement in Spring Boot. My concern, therefore, was that a fix in Spring Boot may only be a point solution. I had wondered if a more general purpose change in Spring Security may be possible.

Comment From: rwinch

Thanks for the fast response @wilkinsona I don't think there really is anything Spring Security can do.

Spring Security needs that Bean and needs it eagerly to properly build the objects. If we did a delayed lookup from the ApplicationContext we would not find the ContentNegotiationStrategy and default it to the HeaderContentNegotationStrategy but this would not be the desired behavior.

These sort of ordering issues creep up in Java Configuration and typically the place that it needs fixed is in the offending configuration. Therefore if a user had this issue they would be required to update their configuration in the same way.

I'm not really sure how this would be triggered in a non-boot scenario because as far as I know Boot is the only way to have delayed loading of beans with the autoconfigurations. Perhaps you can help me to understand how this would occur without Boot.

Comment From: wilkinsona

These sort of ordering issues creep up in Java Configuration and typically the place that it needs fixed is in the offending configuration.

Agreed. What I don't think we agree on, yet, is where that offending configuration is. I had thought that Spring Security could use @Lazy to prevent it from eagerly creating the ContentNegotiationStrategy.

I'm not really sure how this would be triggered in a non-boot scenario because as far as I know Boot is the only way to have delayed loading of beans with the autoconfigurations. Perhaps you can help me to understand how this would occur without Boot.

Auto-configuration doesn't delay the loading of any beans, it just orders the configuration classes such that auto-configured bean defintions are processed after all of a user's bean definitions. In other words, any configuration arrangement that exists in Boot could be created in a non-Boot application.

Here's a significantly smaller application that reproduces the problem:

@SpringBootApplication
public class Security4202Application {

    public static void main(String[] args) {
        ConversionService conversionService = SpringApplication.run(Security4202Application.class, args)
                .getBean(ConversionService.class);
        conversionService.convert("some input", Thing.class);
    }

    @JsonComponent
    static class ThingDeserializer extends JsonDeserializer<Thing> {

        public ThingDeserializer(ConversionService conversionService) {
        }

        @Override
        public Thing deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            return null;
        }

    }

    @Configuration
    static class MvcConfiguration extends WebMvcConfigurerAdapter {

        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addFormatter(new Formatter<Thing>() {

                @Override
                public String print(Thing object, Locale locale) {
                    return "formatted thing";
                }

                @Override
                public Thing parse(String text, Locale locale) throws ParseException {
                    return new Thing();
                }

            });
        }

    }

    @Configuration
    static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    }

    static class Thing {

    }

}

When run, it should throw an exception indicating that the was no converter available for String to Thing. Interestingly, if we strip out Spring Security (by removing SecurityConfiguration), we can see that the early initialisation of ContentNegotiationStrategy that it triggers was masking a dependency cycle:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  security4202Application.ThingDeserializer defined in file [/Users/awilkinson/dev/workspaces/spring/spring-boot/1.5.x/security-4202/target/classes/com/example/Security4202Application$ThingDeserializer.class]
↑     ↓
|  org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration
↑     ↓
|  org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
↑     ↓
|  org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration
↑     ↓
|  mappingJackson2HttpMessageConverter defined in class path resource [org/springframework/boot/autoconfigure/web/JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration.class]
↑     ↓
|  jacksonObjectMapper defined in class path resource [org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonObjectMapperConfiguration.class]
└─────┘

The cycle happens because the ThingDeserializer depends on the conversion service. The conversion service is produced by Spring MVC. Via Jackson and the Jackson HTTP message converter, the ThingDeserializer is required for the configuration of Spring MVC's message converters. This means that ThingDeserializer consumes the conversion service but also needs to be created so that Spring MVC can be initialized and can create the conversion service.

In short, injecting theConversionService into a JsonComponent or into anything involved with HTTP message conversion is very likely to create a dependency cycle. I'm not sure what, if anything could/should be done in Spring Boot to break this cycle. One obvious step outside of Boot would be to mark the ConversionService dependency in the JsonComponent as @Lazy.

I think it would also be beneficial to change Spring Security so that it doesn't trigger eager initialisation of the ContentNegotiationManager. This would stop it from masking the more obvious underlying problem.

Comment From: rwinch

Thanks for the additional details @wilkinsona!

Perhaps I am mistaken, but I don't think I am getting the same result when removing the Security Configuration. When I remove the Security Configuration from the sample, the error goes away completely. It sounded as though you were getting an error of a circular bean dependency. I am using Boot 1.5.3.RELEASE. Perhaps this is a game of chance with the order components are getting scanned on our different systems?

Another thing I noticed is that if I replace the Security Configuration with the following, I also experience the ConverterNotFoundException.

@Autowired
ContentNegotiationStrategy contentNegotiationStrategy;

Comment From: wilkinsona

@rwinch When you say "the sample" in your comment above, are you referring to Security4202Application that I wrote or the app provided by @jcagarcia?

Comment From: rwinch

@wilkinsona Sorry for being unclear. I am referring to Security4202Application that you provided.

Comment From: wilkinsona

That's a strange one. I have seen a few occasions in the past where the Framework isn't deterministic and things end up running in a different order causing sometimes dramatically different behaviour. I'd guess, and it is only a guess, that this is one of those cases.

Comment From: MrNghia123

I have the same problem.

Comment From: saiya

Having same problem still (Spring Boot 2.2.2.RELEASE).

Are there any workaround without overriding WebSecurityConfigurerAdapter#setContentNegotationStrategy method without @Autowired annotation?

Comment From: jzheaux

Reading the issue log, it appears that the behavior may be non-deterministic. Also, both samples listed in the issue are a bit out of date. Finally, I believe that @rwinch concluded at the time that there was nothing that Spring Security could do:

These sort of ordering issues creep up in Java Configuration and typically the place that it needs fixed is in the offending configuration. Therefore if a user had this issue they would be required to update their configuration in the same way.

That said, @saiya, would you be able to provide a minimal sample that reproduces your issue?