spring mvc one init binder for all controllers

4.6k views Asked by At

I have 5 controllers and i would like to register an InitBinder to all of them.

I know i can add this code to each of them.

@InitBinder
public void initBinder(WebDataBinder binder)
{
    binder.registerCustomEditor(StringWrapper.class, new StringWrapperEditor());
}

But i would like to define it only once (even create a bean of StringWrapperEditor and use it instead of creating new every time.)

I searched SO and some other places but didn't find any answear. Is it even possible?

Im using spring 3.1.1 with java 1.6.

2

There are 2 answers

4
M. Deinum On BEST ANSWER

Implement a PropertyEditorRegistrar which registers all your custom PropertyEditors. Then in your configuration add a ConfigurableWebBindingInitializer which you hookup with the created PropertyEditorRegistrar and hook it to your HandlerAdapter.

public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {
         registry.registerCustomEditor(StringWrapper.class, new StringWrapperEditor());   
    }
}

If you have a <mvc:annotation-driven /> tag in your configuration, the problem is that with this tag you cannot add the WebBindingInitializer to the adapter next to that there is already a ConfigurableWebBindingInitializer added to the pre-configured HandlerAdapter. You can use a BeanPostProcessor to proces and configure the bean.

public class MyPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
        if (bean instanceof RequestMappingHandlerAdapter) {
            WebBindingInitializer wbi = ((RequestMappingHandlerAdapter) bean).getWebBindingInitializer();
            if (wbi == null) {
                wbi = new ConfigurableWebBindingInitializer();
                ((RequestMappingHandlerAdapter) bean).setWebBindingInitializer(wbi);
            }

            if (wbi instanceof ConfigurableWebBindingInitializer) {
                ((ConfigurableWebBindingInitializer) wbi).setPropertyEditorRegistrar(new MyPropertyEditorRegistrar());
            }

        }
    }

}

Requires a bit of work but it is doable. You could also implement your own WebBindingInitializer.

If you don't have the tag you can simply manually configure a RequestMappingHandlerAdapter and wire everything together.

Links

  1. PropertyEditorRegistrar javadoc
  2. ConfigurableWebBindingInitializer javadoc
  3. Reference Guide link
0
silk On

Though the initial question was about Spring 3.1, the following might be useful for those who use newer Spring versions.

One possible option is to move your @InitBinder to @ControllerAdvice, for example

@ControllerAdvice
class InitBinderControllerAdvice {
    @InitBinder
    fun initBinder(dataBinder: WebDataBinder) {
        dataBinder.registerCustomEditor(
            MLQueryOutputFormat::class.java,
            StringToMLQueryOutputFormat()
        )
        dataBinder.registerCustomEditor(
            IDatabaseOps.SortDirection::class.java,
            StringToSortDirection()
        )
    }
}

Regarding ConfigurableWebBindingInitializer, even though it's quite a powerful thing, it requires additional configuration in terms of validation and etc. So pay attention to detail once implementing it. For instance, the following code does the job as per InitBinder, but lacks setting a Validator. As a result, the validation of the rest controller param annotated with @Validated didn't work:

@Configuration
class WebMvcConfig {
    @Bean
    fun configurableWebBindingInitializer(): ConfigurableWebBindingInitializer {
        val initializer = ConfigurableWebBindingInitializer()
        initializer.propertyEditorRegistrars = arrayOf(
            PropertyEditorRegistrar {
                it.registerCustomEditor(
                    MLQueryOutputFormat::class.java,
                    StringToMLQueryOutputFormat()
                )
            }, PropertyEditorRegistrar {
                it.registerCustomEditor(
                    IDatabaseOps.SortDirection::class.java,
                    StringToSortDirection()
                )
            }
        )
        return initializer
    }
}

To add validation, one could do the following:

@Bean
fun configurableWebBindingInitializer(
    @Qualifier("defaultValidator") validator: Validator
): ConfigurableWebBindingInitializer {
    val initializer = ConfigurableWebBindingInitializer()
    initializer.validator = validator
    ...