복붙노트

[SPRING] Spring 3에서 주석과 함께 변환기 및 converterFactories 등록

SPRING

Spring 3에서 주석과 함께 변환기 및 converterFactories 등록

우선 ... 저는 비교적 봄에 새로운 개념입니다. 스프링 3.x를 사용하고 있습니다. SPRING의 XML CONFIGURATION FILES와 비슷하지 않습니다. XML 파일을 업데이트하기 위해 모든 리팩토링을하고 싶지 않습니다 ...

내가 요청한 방식으로 Spring을 구성하려고 시도 중입니다. @ RequestParam / @ RequestBody / @ PathVariable 등을 String 이외의 형식으로 사용하는 경우 spring은 해당 유형으로 올바르게 값을 변환하거나 처리기의 값을 null로 설정합니다. 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>

주석을 사용하여 변환기를 등록 할 수있는 방법이 있습니까?

스프링 XML에 대한 모든 것 (또는 기본적인 내용)을 주석으로 만 처리 할 수 ​​있습니까? XML 구성을 한 번에 없앨 수 있습니까? ... 그리고 어떻게?

해결법

  1. ==============================

    1.Spring에는 Converters에 대한 주석 지원이 없지만 직접 작성할 수 있습니다.

    Spring에는 Converters에 대한 주석 지원이 없지만 직접 작성할 수 있습니다.

    필요한 @AutoRegistered annotation (@AutoRegistered라고 부름)과 @AutoRegistered 주석 (그리고이 등록 서비스를 등록하기위한 일부 XML)을 가진 모든 Spring Bean을 등록하는 일종의 Converter / Formatter Registrar (FormatterRegistrar 구현) 만 있으면됩니다.

    그런 다음이 주석 (및 주석을 작성하기위한 다른 주석으로 컨 비터에 주석을 달아야합니다. 봄 콩) 그게 전부입니다.

    @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 귀하의 enum 변환기에 당신은 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 "";
             }
         }   
    }
    
  2. ==============================

    2.먼저 주석을 정의해야합니다 : TypeConverter

    먼저 주석을 정의해야합니다 : 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);
        }
    }
    }
    

    자세한 내용이 필요한 경우이 블로그 항목을 확인하십시오.

  3. ==============================

    3.Converter Bean의 자동 등록은 @EnableAutoConfiguration이 켜져있을 때 Spring Boot에 의해 제공됩니다 - Spring Boot 기능을 참조하십시오. 이를 위해 별도의 주석 (각 변환기 빈을 @Component로 표시하는 것 외에는 필요 없음)이 필요하지 않습니다.

    Converter Bean의 자동 등록은 @EnableAutoConfiguration이 켜져있을 때 Spring Boot에 의해 제공됩니다 - Spring Boot 기능을 참조하십시오. 이를 위해 별도의 주석 (각 변환기 빈을 @Component로 표시하는 것 외에는 필요 없음)이 필요하지 않습니다.

  4. ==============================

    4.@Ralph에 의해 개략적으로 설명 된 접근 방식은 깔끔하고 그의 대답을 +1했습니다. 또한 @Configuration 지원을 사용하는 대체 접근법을 권장합니다. 기본적으로 xml 대신 Java를 사용하여 Spring Bean을 구성하는 방법입니다. 이 방법을 사용하면 메시지 변환기를 다음과 같이 등록 할 수 있습니다.

    @Ralph에 의해 개략적으로 설명 된 접근 방식은 깔끔하고 그의 대답을 +1했습니다. 또한 @Configuration 지원을 사용하는 대체 접근법을 권장합니다. 기본적으로 xml 대신 Java를 사용하여 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());
                    ...
        }
    
     }
    
  5. ==============================

    5.Spring MVC 3.2를 사용하면 DefaultFormattingConversionService를 확장하는 변환 서비스 클래스를 생성 할 수있다.

    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 config에 지정하십시오.

    dispatcher-servlet.xml

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

    6.이것이 Spring 3에서 작동하는지 확신 할 수 없지만 이것이 Spring 4의 해결책입니다.

    이것이 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;
        }
    }
    
  7. from https://stackoverflow.com/questions/13778099/register-converters-and-converterfactories-with-annotations-in-spring-3 by cc-by-sa and MIT license