Register converters and converterFactories with annotations in Spring 3
Spring does not have annotation support for Converters, but you can build your own.
All you need is an custom qualifier annotation (lets call it @AutoRegistered
) and some kind of Converter/Formatter Registrar (implements FormatterRegistrar
) that registers all the Spring Beans with this @AutoRegistered
annotation (and some xml to register this registration service).
Then you need to annotate your conveter with this annotation (and some other annotation to make it a spring bean) and that is all.
@AutoRegistered
annotation:
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface AutoRegistered {}
Registration service:
import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.convert.converter.Converter;import org.springframework.format.FormatterRegistrar;import org.springframework.format.FormatterRegistry;public class AutoregisterFormatterRegistrar implements FormatterRegistrar { /** * All {@link Converter} Beans with {@link AutoRegistered} annotation. * If spring does not find any matching bean, then the List is {@code null}!. */ @Autowired(required = false) @AutoRegistered private List<Converter<?, ?>> autoRegisteredConverters; @Override public void registerFormatters(final FormatterRegistry registry) { if (this.autoRegisteredConverters != null) { for (Converter<?, ?> converter : this.autoRegisteredConverters) { registry.addConverter(converter); } } }}
XML configuration for the registrar:
<bean id="applicationConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="formatterRegistrars"> <set> <bean class="AutoregisterFormatterRegistrar" autowire="byType" /> </set> </property></bean>
BTW for your enum converter you do not need a ConversionFactory
- a simple converter is enough:
@AutoRegistered@Componentpublic class EnumConverter implements Converter<Enum<?>, String> { /** Use the same immutable value instead of creating an new array every time. */ private static final Object[] NO_PARAM = new Object[0]; /** The prefix of all message codes. */ private static final String PREFIX = "label_"; /** The separator in the message code, between different packages as well as between package can class. */ private static final String PACKAGE_SEPARATOR = "_"; /** The separator in the message code, between the class name and the enum case name. */ private static final String ENUM_CASE_SEPARATOR = "_"; /** The message source. */ private MessageSource messageSource; @Autowired public EnumConverter(final MessageSource messageSource) { if (messageSource == null) { throw new RuntimeException("messageSource must not be null"); } this.messageSource = messageSource; } @Override public String convert(final Enum<?> source) { if (source != null) { String enumValueName = source.name(); String code = PREFIX + source.getClass().getName().toLowerCase(). replace(".", PACKAGE_SEPARATOR) + ENUM_CASE_SEPARATOR + enumValueName.toLowerCase(); String message = messageSource.getMessage(code, NO_PARAM, enumValueName, LocaleContextHolder.getLocale()); return message; } else { return ""; } } }
Automatic registration of Converter beans is also provided by Spring Boot when @EnableAutoConfiguration
is turned on - see Spring Boot features. It appears that no additional annotations (other than marking each converter bean as a @Component
) is required for this.
First you have to define an annotation: TypeConverter
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface TypeConverter {}
Then you have to register the conversion service and add all of the beans that have the annotation. This will be done with the following post processor:
public class ConverterRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { registry.registerBeanDefinition("conversionService", BeanDefinitionBuilder.rootBeanDefinition(ConversionServiceFactoryBean.class).getBeanDefinition());}public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Map<String, Object> beansWithAnnotation = beanFactory.getBeansWithAnnotation(TypeConverter.class); Collection converters = beansWithAnnotation.values(); DefaultConversionService conversionService = (DefaultConversionService) beanFactory.getBean("conversionService"); for (Object converter : converters) { conversionService.addConverter((Converter<?, ?>) converter); }}}
If you need more details check this blog entry