Skip to content

@JsonComponent cannot use ConversionService without marking it @Lazy #9409

@wilkinsona

Description

@wilkinsona

This application will fail to start:

package com.example;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.core.convert.ConversionService;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

@SpringBootApplication
public class JsonComponentConversionServiceCycle {

	public static void main(String[] args) {
		SpringApplication.run(JsonComponentConversionServiceCycle.class, args)
				.getBean(ConversionService.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;
		}

	}

	static class Thing {

	}

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

Description:

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

┌─────┐
|  jsonComponentConversionServiceCycle.ThingDeserializer defined in file [/Users/awilkinson/dev/workspaces/spring/spring-boot/1.5.x/security-4202/target/classes/com/example/JsonComponentConversionServiceCycle$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]
└─────┘

If you bring Spring Security into the mix (by extending WebSecurityConfigurerAdapter) the problem will appear to have gone away as some eager initialisation that Spring Security triggers manages to break the cycle. The cycle is broken by Spring Security triggering the creation of the conversion service before any WebMvcConfigurerAdapter beans have been injected into DelegatingWebMvcConfiguration. This has the unwanted side-effect of losing any conversion service-related configuration that would have been performed by those beans. There's a Spring Security issue that can hopefully be used to prevent it from triggering that early initialisation.

A user can avoid the problem by marking the ConversionService dependency as @Lazy. It would be better if they didn't have to do that, though. One solution that appears to work is for the HttpMessageConverters dependency in WebMvcAutoConfigurationAdapter to be marked as @Lazy.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions