首页 文章

在Spring 3中注册带注释的转换器和converterFactories

提问于
浏览
8

首先......我在Spring中相对较新,我使用spring 3.x而且我不喜欢Spring的XML配置文件...我不希望我做的每一次重构,都要运行XML文件进行更新......

我试图以一种方式配置spring,对于任何请求,如果我在我的hadlers中有一些@RequestParam / @RequestBody / @ PathVariable等类型而不是String类型,spring会正确地将值转换为该类型或者将null放入handler's args(我从不在处理程序参数中使用原始类型) . 到现在为止还挺好 ...

到目前为止,我已经注册了所有的converter / converterFactory类,如下所示:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
     <property name="converters">
         <list>
             <!-- converters is a set of both converters and converterfactories -->
             <bean class="controller.converters.enumConverter" />
             <bean class="controller.converters.integerConverter" />
             <bean class="controller.converters.objects.FooConverter" />
             ...
         </list>
     </property>
 </bean>

有没有办法注册带注释的转换器?

关于spring XML的任何东西(或者只是基本的东西)都可以仅使用注释完成,并且一劳永逸地摆脱XML配置吗? ... 如何?

6 回答

  • 9

    @Ralph概述的方法很整洁,我的答案是1 . 我还建议使用 @Configuration support的替代方法 - 本质上是一种使用Java而不是xml配置Spring bean的方法 . 使用这种方法,可以通过以下方式注册消息转换器:

    @Configuration
     @EnableWebMvc
     @ComponentScan(...)
     public class CustomConfig extends WebMvcConfigurerAdapter {
    
    
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(new EnumConverter());
                    converters.add(new FooConverter());
                    ...
        }
    
     }
    
  • 6

    Spring没有对转换器的注释支持,但您可以构建自己的 .

    您只需要一个自定义限定符注释(让我们称之为 @AutoRegistered )和某种类型的Converter / Formatter注册器(实现FormatterRegistrar),它使用此 @AutoRegistered 注释注册所有Spring Bean(以及一些xml来注册此注册服务) .

    然后你需要用这个注释(和一些其他的注释使它成为一个 spring beans )来注释你的对流器,这就是全部 .

    @AutoRegistered 注释:

    @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface AutoRegistered {}
    

    报名服务:

    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配置:

    <bean id="applicationConversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatterRegistrars">
            <set>
                <bean
                    class="AutoregisterFormatterRegistrar"
                    autowire="byType" />
            </set>
        </property>
    </bean>
    

    BTW为您的枚举转换器,您不需要 ConversionFactory - 一个简单的转换器就足够了:

    @AutoRegistered
    @Component
    public 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 "";
             }
         }   
    }
    
  • 5

    使用Spring MVC 3.2,您可以创建一个扩展DefaultFormattingConversionService的转换服务类,例如

    ApplicationConversionService.java

    import org.springframework.format.support.DefaultFormattingConversionService;
    import org.springframework.stereotype.Component;
    
    @Component("conversionService")
    public class ApplicationConversionService extends DefaultFormattingConversionService  { 
    
        public ApplicationConversionService(){
            //DefaultFormattingConversionService's default constructor
            //creates default formatters and converters
            super(); //no need for explicit super()?
    
            //add custom formatters and converters
            addConverter(new MyConverter());
        }
    
    }
    

    并在spring配置中指定它,例如

    dispatcher-servlet.xml

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

    首先,您必须定义注释:TypeConverter

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface TypeConverter {
    }
    

    然后,您必须注册转换服务并添加具有注释的所有bean . 这将使用以下后处理器完成:

    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);
        }
    }
    }
    

    如果您需要更多详细信息,请查看blog entry

  • 1

    我不确定这是否适用于Spring 3,但这是Spring 4的解决方案:

    @Configuration
    @EnableWebMvc
    class WebMvcContext extends WebMvcConfigurerAdapter {
    
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(new DateConverter("yyyy-MM-dd HH:mm:ss"));
            //registry.addConverter(anotherConverter);
        }
    }
    

    DateConverter是一个自定义转换器:

    public class DateConverter implements Converter<String, Date>{
        private static final Logger LOGGER = LoggerFactory.getLogger(DateConverter.class);
        private final String dateFormat;
        private final SimpleDateFormat formatter;
        public DateConverter(String dateFormatPattern) {
            this.dateFormat = dateFormatPattern;
            this.formatter = new SimpleDateFormat(dateFormatPattern);
        }
    
        @Override
        public Date convert(String source) {
            Date date = null;
            try {
                date = formatter.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    
  • 0

    @EnableAutoConfiguration打开时,Spring Boot也会自动注册Converter Bean - 请参阅Spring Boot features . 似乎没有额外的注释(除了将每个转换器bean标记为 @Component 之外) .

相关问题