Here's a very quick how-to for configuring Spring-MVC (Spring 3.1.x) to use a global set of formatters for converting (data-binding) web-request and form parameters for use in Controllers, rather than having to have an @InitBinder annotated method in all your @Controller's.

In your spring-web configuration:

<mvc:annotation-driven conversion-service="conversionService"/>

<!-- just to show that we can wire other beans into our registrar -->
<bean id="serviceA" class="com.sjl.myproject.ServiceA"/>
<bean id="serviceB" class="com.sjl.myproject.ServiceB"/>

<!-- Binding -->    
<bean 
  id="customFormatterRegistrar" 
  class="com.sjl.myproject.web.config.CustomFormatterRegistrar">
  <constructor-arg ref="serviceA"/>        
  <constructor-arg ref="serviceB"/>
</bean>

<bean 
  id="conversionService"
  class="
    org.springframework.format.support.FormattingConversionServiceFactoryBean">
  <property name="formatterRegistrars">
    <set>
      <ref local="customFormatterRegistrar"/>
    </set>
  </property>
</bean>

Be sure to add the "conversion-service" attribute (pointing at your conversionService bean) to the <mvc:annotation-driven> element, otherwise it won't work!

The CustomFormatterRegistrar class:

package com.sjl.myproject.web.config;

import org.springframework.format.*;
import com.sjl.myproject.*;

public class CustomFormatterRegistrar implements FormatterRegistrar {
    private ServiceA serviceA;
    private ServiceB serviceB;      

    // construct the registrar with other spring-beans as constructor args
    public CustomFormatterRegistrar(
        ServiceA aServiceA,
        ServiceB aServiceB) {
        serviceA = aServiceA;
        serviceB = aServiceB;
    }

    @Override
    public void registerFormatters(FormatterRegistry aRegistry) {
        aRegistry.addFormatter(new SomeTypeFormatter(serviceA));
        aRegistry.addFormatter(new OtherTypeFormatter(serviceB))
    }
}

An example formatter:

package com.sjl.myproject.web.config;

import java.text.*;
import java.util.*;

import org.springframework.format.Formatter;

import com.sjl.myproject.*;

public class SomeTypeFormatter implements Formatter&lt;SomeType> {
    private ServiceA serviceA;

    public SomeTypeFormatter(ServiceA aServiceA) {
        serviceA = aServiceA;
    }

    @Override
    public String print(SomeType aSomeType, Locale aLocale) {
        return aSomeType..; // produce some string-based identifier
    }

    @Override
    public SomeType parse(String aText, Locale aLocale) throws ParseException {
        return serviceA.lookupByNameOrIdOrSomething(aText);
    }
}

And a Controller that benefits from it:

package com.sjl.myproject.web.controllers;

import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

import com.sjl.myproject.*;

@Controller
public class SomeController {
    public static final String URL = "path/with/{param1}/and/{param2}";

    @RequestMapping(SomeController.URL)
    public String blah(
        @PathVariable SomeType param1, 
        @PathVariable OtherType param2) {

        // .. do stuff with our typed params

        return "view-name";
    }
}
blog comments powered by Disqus