Gidhub BE Developer

Spring 프레임워크 핵심 기술 - Converter와 Formatter 2부

2019-09-03
goodGid

이 글의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성하였습니다.


  • PropertyEditor를 사용하면

  • DataBinder 인터페이스를 통해 데이터 바인딩이 이뤄진다.


  • Converter와 Formatter를 사용하면

  • ConversionService 인터페이스를 사용하여 데이터 바인딩이 이뤄진다.


  • 만약 Converter와 Formatter를 Config에 등록하여 사용한다면

  • ConversionService 인터페이스를 사용한다고 할 수 있다.

DefaultFormattingConversionService

Spring MVC 기준

  • DefaultFormattingConversionService는

  • ConversionService 타입의 대표적인 빈 클래스로 사용된다.


  • DefaultFormattingConversionService에는 기본적으로

  • FormatterRegistryConversionService를 구현했다.


  • 추가적으로

  • 기본적인 Converter와 Formatter 또한 등록되어 있다.


  • DefaultFormattingConversionService의 구조는 다음과 같다.

  • 위 그림에서 알 수 있듯이

  • DefaultFormattingConversionService는 FormatterRegistry를 상속

  • FormatterRegistry는 ConverterRegistry를 상속하는 관계를 갖는다.


  • 실제로 FormatterRegistry 소스코드를 보면

  • ConverterRegistry를 상속하는 것을 볼 수 있다.

public interface FormatterRegistry extends ConverterRegistry {
    void addFormatter(Formatter<?> var1);

    void addFormatterForFieldType(Class<?> var1, Formatter<?> var2);

    void addFormatterForFieldType(Class<?> var1, Printer<?> var2, Parser<?> var3);

    void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> var1);
}
  • 그렇기 때문에

  • FormatterRegistry에 Converter 등록이 가능하다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
       registry.addConverter(new StringToEventConverter());
    }
}

AppRuner를 통해 실제로 conversionService 출력해보자.

@Component
public class AppRuner implements ApplicationRunner {

    @Autowired
    private ConversionService conversionService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("[1] : " + conversionService);
        System.out.println("[2] : " + conversionService.getClass().toGenericString());
        System.out.println("[3] : " + conversionService.getClass().toString());
    }
}

출력 결과

[1] : ConversionService converters =
	@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@bae47a0,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@3b65e559
    ...
    org.springframework.core.convert.support.StringToArrayConverter@61e3a1fd
	org.springframework.core.convert.support.StringToCollectionConverter@315df4bb

// WebConversionService 출력
[2] : public class org.springframework.boot.autoconfigure.web.format.WebConversionService

// WebConversionService 출력
[3] : class org.springframework.boot.autoconfigure.web.format.WebConversionService
  • 그런데 기대했던 DefaultFormattingConversionService 가 아닌

  • WebConversionService Class가 나온다.


  • 이유가 뭘까?

  • 코드를 다시 보자.

public class WebConversionService extends DefaultFormattingConversionService {
    private static final boolean JSR_354_PRESENT = ClassUtils.isPresent("javax.money.MonetaryAmount", WebConversionService.class.getClassLoader());
    private static final boolean JODA_TIME_PRESENT = ClassUtils.isPresent("org.joda.time.LocalDate", WebConversionService.class.getClassLoader());
    private final String dateFormat;

    public WebConversionService(String dateFormat) {
        super(false);
        this.dateFormat = StringUtils.hasText(dateFormat) ? dateFormat : null;
        if (this.dateFormat != null) {
            this.addFormatters();
        } else {
            addDefaultFormatters(this);
        }

    }

    private void addFormatters() {
        this.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
        if (JSR_354_PRESENT) {
            this.addFormatter(new CurrencyUnitFormatter());
            this.addFormatter(new MonetaryAmountFormatter());
            this.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
        }

        this.registerJsr310();
        if (JODA_TIME_PRESENT) {
            this.registerJodaTime();
        }

        this.registerJavaDate();
    }

    ...

    private void registerJavaDate() {
        DateFormatterRegistrar dateFormatterRegistrar = new DateFormatterRegistrar();
        if (this.dateFormat != null) {
            DateFormatter dateFormatter = new DateFormatter(this.dateFormat);
            dateFormatterRegistrar.setFormatter(dateFormatter);
        }

        dateFormatterRegistrar.registerFormatters(this);
    }
}
  • Spring Boot에서는

  • DefaultFormattingConversionService를 상속하여 만든

  • WebConversionService를 제공한다.

  • WebConversionService = DefaultFormattingConversionService + 추가 기능


Spring Boot일 경우

  • Spring MVC환경에서는

  • Config에

  • Formatter와 Converter를 직접 등록하여 사용했다.



  • Formatter와 Converter를

  • 직접등록하는 코드를 제거하고

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
    //    registry.addConverter(new StringToEventConverter());
    //    registry.addFormatter(new EventFormatter());
    }
}
  • Formatter와 Converter를

  • @Component 선언을 통해

  • Bean으로 등록한 후

  • TC를 돌려도 성공하는 것을 확인 할 수 있다.

public class EventConverter {
    
    @Component
    public static class StringToEventConverter implements Converter<String, Event> {
        @Override
        public Event convert(String s) {
            return new Event(Integer.parseInt(s));
        }
    }

    @Component
    public static class EventToStringConvert implements Converter<Event, String> {
        @Override
        public String convert(Event event) {
            return event.getId().toString();
        }
    }
}

Reference


Recommend

Index