deamon
deamon

Reputation: 92437

How to avoid duplicate Converter in Spring ConversionService?

I have a custom StringToBooleanConverter that should replace the default converter coming with Spring. So the source and target types are exactly the same. But instead of replacing the existing Spring converter, my converter is added. If I debug the application, I can see both converters in the same map entry of the Map ConversionService#converters.

The ConversionService is configured like this:

@Bean
open fun conversionService(converters: Set<Converter<*, *>>) =
        ConversionServiceFactoryBean().apply { setConverters(converters) }

@Bean
open fun stringToBooleanConverter() = MyStringToBooleanConverter()

// some more converters not relevant here ...

The problem is that sometimes the wrong converters gets used.

How can I remove/replace/hide/deactivate the converter delivered by Spring?

Upvotes: 4

Views: 1736

Answers (2)

deamon
deamon

Reputation: 92437

The trick is to define a custom ConversionServiceFactoryBean, to override the method createConversionService, and to remove the StringToBooleanConverter registered by Spring itself:

class ConversionServiceFactoryWithoutStringToBooleanConverter : ConversionServiceFactoryBean() {

    override fun createConversionService(): GenericConversionService {
        val conversionService = super.createConversionService()
        conversionService.removeConvertible(String::class.java, java.lang.Boolean::class.java)
        return conversionService
    }
}

However, in this case it is not necessary to remove the Spring converter because if there is more than one converter for a certain source and target type Spring tries them in order and converters registered by the user come first. The behavior that drove me to this investigation was in fact related to another bug not related to the ConversionService.

Upvotes: 3

John Humphreys
John Humphreys

Reputation: 39294

Update

If you're running Spring Boot, you can start your application like this and get a bean as follows.

ApplicationContext ctx = SpringApplication.run(Main.class, args);
DispatcherServlet servlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");

You can list all the beans with:

ctx.getBeanDefinitionNames()

Note that most are just named as the class name with a lower case first letter (would assume "stringToBooleanConverter" for you). Some have the package name in front and then that. You can check this out in a debugger easily :).

You can find a bean of a specific class type like this (note that this definition returns null in my case; so this dependency doesn't seem to be in my application):

ctx.getBean(StringToBooleanConverter.class)

You can remove the bean with:

((AnnotationConfigEmbeddedWebApplicationContext)
     ctx).removeBeanDefinition("yourBeanName")

Original Suggestion

This ties into a related question here: https://stackoverflow.com/a/12447211/857994. as realistically, you just want to remove the existing bean.

You can get a reference to the bean factory and remove the old bean from it manually. I think the most direct method would probably be the one suggested in the comment though: "Just implement BeanFactoryAware and then cast BeanFactory with DefaultListableBeanFactory".

I don't believe there is a way to stop the provision of the bean in the first place since it comes from spring itself. You can annotate your own beans to avoid autowiring, I don't know how you could achieve that on a spring-provided bean though.

Upvotes: 0

Related Questions