2012-12-08 9 views
7

Prima di tutto ... Sono relativamente nuovo in primavera, utilizzo la molla 3.xe I FILETTI DI CONFIGURAZIONE XML NON MI PIACCIONO DELLA PRIMAVERA ... Non desidero per ogni refactoring che faccio, per eseguire il file XML per gli aggiornamenti ...Convertitori di registro e convertitori Aziende con annotazioni nella primavera 3

Sto provando a configurare Spring in un modo che per qualsiasi richiesta, se ho qualche @ RequestParam/@ RequestBody/@ PathVariable ecc con un tipo diverso da String nei miei hadler , spring convertirà correttamente i valori in quel tipo o inserirà null negli argomenti del gestore (non uso mai i tipi primitivi negli argomenti del gestore). Fin qui tutto bene ...

Fino ad ora ho registrato tutte le classi/converterFactory convertitore come questo:

<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> 

Esiste un modo per registrare convertitori con annotazioni?

Qualunque cosa (o solo le cose di base) sull'XML di primavera può essere fatto solo con le annotazioni ed eliminare una volta per tutte la configurazione XML? ... e come?

risposta

9

Spring non ha supporto di annotazione per i convertitori, ma è possibile crearne uno proprio.

Tutto ciò che serve è un'annotazione di qualificazione su misura (consente di chiamare @AutoRegistered) e una sorta di convertitore/Formatter Registrar (attrezzi FormatterRegistrar) che registra tutti i fagioli di primavera con questa @AutoRegistered annotazioni (e un po 'XML per registrare questo servizio di registrazione) .

Quindi è necessario annotare il tuo conveter con questa annotazione (e qualche altra annotazione per renderlo un bean spring) e questo è tutto.

@AutoRegistered annotazione:

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

servizio di registrazione:

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

di configurazione XML per il cancelliere:

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

BTW per il vostro convertitore enum non occorre un ConversionFactory - un è sufficiente un semplice convertitore:

@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? Immagino tu intenda FormatterRegistry ... Elenco È > AutoRegisteredConverters riempito automaticamente? ps. La soluzione ConverterFactory per enumerare è più compatta ... controlla questa [implementazione] (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert -ConverterFactory-SPI) – ApollonDigital

+0

È FormatterRegistrar (una funzione Spring 3.1) http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/format/FormatterRegistrar.html – Ralph

5

L'approccio delineato da @Ralph è accurato, ho fatto +1 sulla sua risposta. Consentitemi anche di suggerire un approccio alternativo che utilizza @Configurationsupport - essenzialmente un modo per configurare i bean Spring utilizzando Java anziché xml. Con questo approccio i convertitori ai messaggi possono essere registrati in questo modo:

@Configuration 
@EnableWebMvc 
@ComponentScan(...) 
public class CustomConfig extends WebMvcConfigurerAdapter { 


    @Override 
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
     converters.add(new EnumConverter()); 
       converters.add(new FooConverter()); 
       ... 
    } 

} 
+0

Grazie per il risposta. Non penso che in questo modo risolvi il problema. Anche se ti sbarazzi di xml, hai ancora bisogno di aggiornamenti sui refactories. – ApollonDigital

+0

+1 Questo ha funzionato come un fascino per me e mi sono completamente liberato della configurazione XML con questo. –

6

In primo luogo è necessario definire un'annotazione: TypeConverter

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

Poi si deve registrare il servizio di conversione e aggiungere tutti i fagioli che avere l'annotazioneCiò sarà fatto con il seguente post processore:

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

Se avete bisogno di maggiori dettagli si veda questo blog entry

+1

Benvenuto in Stack Overflow! Grazie per aver postato la tua risposta! Si prega di leggere attentamente le [FAQ sulla promozione di sé] (http://stackoverflow.com/faq#promotion) attentamente. Si noti inoltre che * è * richiesto * di pubblicare un disclaimer ogni volta che si collega al proprio sito/prodotto. –

1

Con Spring MVC 3.2, è possibile creare una classe di servizio di conversione che estende la DefaultFormattingConversionService esempio

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 specificare nella configurazione primavera es

dispatcher-servlet.xml

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

Registrazione automatica di fagioli Converter è fornito anche da Primavera Boot quando @EnableAutoConfiguration è acceso - vedi Spring Boot features. Sembra che non siano necessarie annotazioni aggiuntive (oltre alla marcatura di ciascun bean convertitore come @Component).

+0

Suggerimento utile. sai come abilitare questo nel test di junit? – Heri

+0

Sory @Heri, mi sono imbattuto in questo quando ho visto il codice sorgente di un altro progetto, ma Spring Boot non è utilizzato nel progetto a cui sto lavorando e ho finito per cercare una soluzione più vicina alla risposta accettata. Quindi non potrei consigliarti sui test di jUnit (supponiamo che qui intendi test di integrazione che usano jUnit?) –

+0

Sì, esattamente. Non sono riuscito ad avere i convertitori registrati automaticamente. E dal momento che non ci sono riuscito non l'ho provato né in un normale bootrun dell'applicazione perché ho ripristinato le modifiche al codice (ho una soluzione simile a gmateo con un'annotazione personalizzata che viene aggiunta al primaverile creato da ConversionService (recuperato in getConverters()) :. – Heri

0

Non sono sicuro se questo funziona a Spring 3, ma questa è la soluzione per la primavera 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 è un convertitore personalizzato:

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; 
    } 
} 
Problemi correlati