2012-12-08 50 views
7

首先...我在春天比較新,我使用spring 3.x和我不喜歡SPRING的XML配置文件...我不想爲每個重構做我,運行到XML文件更新...在Spring 3中使用註釋註冊轉換器和轉換器工廠

我試圖配置春天的方式,如果我有一些@ RequestParam/@ RequestBody/@ PathVariable等與字符串在我的哈勒,spring會正確地將值轉換爲該類型,或者將null放入處理程序的參數(我從不在處理程序參數中使用原始類型)。到目前爲止好...

到現在爲止我已經註冊的所有轉換器/ 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配置? ... 如何?

回答

9

Spring對轉換器沒有註釋支持,但可以自己構建。

所有你需要的是一個自定義的限定註釋(可以稱之爲@AutoRegistered)和某種轉換器/格式化註冊處處長(實現FormatterRegistrar)的,其登記所有的Spring Bean與此@AutoRegistered註釋(和一些XML註冊該註冊服務) 。

然後你需要使用這個註解(以及其他一些註釋使它成爲spring bean的 )來註釋你的conveter,這就是全部。

@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 ""; 
     } 
    } 
} 
+0

FormatterRegistrar?我想你的意思是FormatterRegistry ... Is List > autoRegisteredConverters自動填充? ps。 ConverterFactory的枚舉解決方案更加緊湊...檢查這[實施](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert -ConverterFactory-SPI) – ApollonDigital

+0

它是FormatterRegistrar(Spring 3.1特性)http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/format/FormatterRegistrar.html – Ralph

5

@Ralph概述的方法很整潔,我對他的答案+1了。讓我還推薦一種替代方法,它使用@Configurationsupport - 本質上是一種使用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()); 
       ... 
    } 

} 
+0

感謝響應。我不認爲這種方式解決了這個問題。儘管你擺脫了xml,但你仍然需要在重構時更新。 – ApollonDigital

+0

+1這對我來說就像一個魅力,我完全擺脫了這個XML配置。 –

6

首先,你必須定義一個註釋:TypeConverter的

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

然後,你必須註冊轉換服務,並添加所有的豆子是有註釋。這將具有以下後置處理器來完成:

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

歡迎來到Stack Overflow!感謝您發佈您的答案!請務必仔細閱讀[自助推廣常見問題](http://stackoverflow.com/faq#promotion)。另請注意,每次鏈接到您自己的網站/產品時,您都必須*發佈免責聲明。 –

1

使用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()); 
    } 

} 

在春天的配置指定它e.g

調度-servlet.xml中

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

轉換豆@EnableAutoConfiguration開啓時也是由彈簧引導提供的自動註冊 - 見Spring Boot features。看起來這不需要額外的註釋(除了將每個轉換器bean標記爲@Component)。

+0

有用的提示。你知道如何在junit測試中啓用它嗎? – Heri

+0

Sory @Heri,當我查看另一個項目的源代碼時遇到了這個問題,但我正在開發的項目中沒有使用Spring Boot,而且我最終選擇了接近於接受答案的解決方案。所以不能提供關於jUnit測試的建議(假設你在這裏是指使用jUnit的集成測試?) –

+0

是的,確切地說。我沒有成功讓轉換器自動註冊。而且由於我沒有成功,我沒有在應用程序的正常引導程序中嘗試它,因爲我恢復了代碼更改(我有一個類似於gmateo的解決方案,它具有添加到Spring創建的ConversionService中的自定義註釋(在getConverters()):. – Heri

0

我不知道,如果這個工程在春季3但這是春季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; 
    } 
}