스프링 MVC 유형 변환 : PropertyEditor 또는 Converter? 바인딩을 원한다면 여전히 xml config에 3

Spring MVC에서 데이터를 바인딩하고 변환하는 가장 쉽고 간단한 방법을 찾고 있습니다. 가능한 경우 XML 구성을 수행하지 않고

지금까지 PropertyEditors를 다음 과 같이 사용했습니다 .

public class CategoryEditor extends PropertyEditorSupport {

    // Converts a String to a Category (when submitting form)
    @Override
    public void setAsText(String text) {
        Category c = new Category(text);
        this.setValue(c);
    }

    // Converts a Category to a String (when displaying form)
    @Override
    public String getAsText() {
        Category c = (Category) this.getValue();
        return c.getName();
    }

}

...
public class MyController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Category.class, new CategoryEditor());
    }

    ...

}

간단합니다. 두 변환 모두 동일한 클래스에 정의되어 있으며 바인딩은 간단합니다. 모든 컨트롤러에서 일반 바인딩을 원한다면 여전히 xml config에 3 줄을 추가 할 수 있습니다 .


그러나 Spring 3.x는 Converters를 사용하여 새로운 방법을 도입했습니다 .

스프링 컨테이너 내에서이 시스템은 PropertyEditors의 대안으로 사용될 수 있습니다

“최신 대안”이기 때문에 변환기를 사용하고 싶다고 가정 해 봅시다. 두 개의 변환기 를 만들어야합니다 .

public class StringToCategory implements Converter<String, Category> {

    @Override
    public Category convert(String source) {
        Category c = new Category(source);
        return c;
    }

}

public class CategoryToString implements Converter<Category, String> {

    @Override
    public String convert(Category source) {
        return source.getName();
    }

}

첫 번째 단점 : 두 가지 수업을 만들어야합니다. 이점 : 일반성 덕분에 캐스팅 할 필요가 없습니다.

그렇다면 어떻게 간단히 데이터를 변환기에 바인딩합니까?

두 번째 단점 : 컨트롤러에서 간단한 방법 (주석 또는 기타 프로그래밍 기능)을 찾지 못했습니다 someSpringObject.registerCustomConverter(...);.

내가 찾은 유일한 방법은 지루하고 단순하지 않으며 일반적인 크로스 컨트롤러 바인딩에만 관한 것입니다.

  • XML 설정 :

    <bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="somepackage.StringToCategory"/>
                <bean class="somepackage.CategoryToString"/>
            </set>
        </property>
    </bean>
    
  • Java 설정 ( Spring 3.1 이상에서만 ) :

    @EnableWebMvc
    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Override
        protected void addFormatters(FormatterRegistry registry) {
            registry.addConverter(new StringToCategory());
            registry.addConverter(new CategoryToString());
        }
    
    }
    

이 모든 단점을 가지고 왜 변환기를 사용 하는가? 뭔가 빠졌습니까? 내가 모르는 다른 트릭이 있습니까?

PropertyEditors를 계속 사용하고 싶습니다 … 바인딩이 훨씬 쉽고 빠릅니다.



답변

이 모든 단점을 가지고 왜 변환기를 사용 하는가? 뭔가 빠졌습니까? 내가 모르는 다른 트릭이 있습니까?

아니요, PropertyEditor와 Converter를 각각 매우 선언하고 등록하는 방법을 매우 포괄적으로 설명했다고 생각합니다.

내 생각에 PropertyEditors의 범위는 제한되어 있습니다. String을 형식으로 변환하는 데 도움이되며이 문자열은 일반적으로 UI에서 제공되므로 @InitBinder를 사용하여 WebDataBinder를 사용하여 PropertyEditor를 등록하는 것이 좋습니다.

반면에 변환기는 더 일반적이며 UI 관련 변환 (문자열 대 대상 유형)뿐만 아니라 시스템의 모든 변환을위한 것입니다. 예를 들어 Spring Integration은 메시지 페이로드를 원하는 유형으로 변환하기 위해 변환기를 광범위하게 사용합니다.

UI 관련 흐름에 대해서는 PropertyEditors가 여전히 특정 명령 속성에 대해 사용자 정의 작업을 수행 해야하는 경우에 적합하다고 생각합니다. 다른 경우에는 Spring 참조에서 권장 사항을 가져 와서 대신 변환기를 작성하십시오 (예 : Long id에서 엔티티로 샘플로 변환).


답변

  1. String으로 /에서 String으로 변환 할 때는 변환기 대신 formatter (implement org.springframework.format.Formatter )를 사용하십시오. 그것은이 인쇄 (…)구문 분석을 (…) 방법, 그래서 당신은 하나의 클래스 만 대신 두 필요는 없다. 이를 등록하려면 ConversionServiceFactoryBean 대신 변환기와 포맷터를 모두 등록 할 수있는 FormattingConversionServiceFactoryBean을 사용하십시오 .
  2. 새로운 포매터에는 다음과 같은 몇 가지 추가 이점이 있습니다.
    • 포맷터 인터페이스는 locale 객체를 print (…)parse (…) 메서드로 제공하므로 문자열 변환은 로케일 구분 가능
    • 사전 등록 된 포맷터 외에도 FormattingConversionServiceFactoryBean 에는 편리한 사전 등록 된 AnnotationFormatterFactory 객체 가 함께 제공되어 주석을 통해 추가 형식 매개 변수를 지정할 수 있습니다. 예를 들면 다음과 같습니다.
      @RequestParam@DateTimeFormat (pattern = “MM-dd-yy”)LocalDate baseDate …
      자체 AnnotationFormatterFactory 클래스 를 만드는 것은 그리 어렵지 않습니다 . 간단한 예제는 Spring의 NumberFormatAnnotationFormatterFactory 를 참조하십시오 . 컨트롤러 별 포맷터 / 에디터가 필요하지 않다고 생각합니다. 모든 컨트롤러에 하나의 ConversionService 를 사용 하고 주석을 통해 형식을 사용자 정의하십시오.
  3. 컨트롤러 특정 문자열 변환이 여전히 필요한 경우 가장 간단한 방법은 여전히 ​​사용자 정의 속성 편집기를 사용하는 것입니다. ( 내 @InitBinder 메소드 에서 ‘ binder.setConversionService (…) ‘ 를 호출하려고 시도했지만 바인더 객체에 이미 ‘전역’변환 서비스가 설정되어 있기 때문에 실패합니다. 컨트롤러 당 변환 클래스는 권장하지 않습니다. 봄 3).

답변

가장 간단하지만 (퍼시스턴스 프레임 워크를 사용한다고 가정 할 때) 완벽한 방법은 아니지만 ConditionalGenericConverter메타 데이터를 사용하여 엔티티를 변환하는 인터페이스를 통해 일반 엔티티 변환기를 구현하는 것입니다.

예를 들어, JPA를 사용하는 경우이 변환기는 지정된 클래스에 @Entity주석 이 있는지 확인하고 @Id주석이있는 필드를 사용하여 정보를 추출하고 제공된 문자열 값을 조회 ID로 사용하여 자동으로 조회를 수행 할 수 있습니다.

public interface ConditionalGenericConverter extends GenericConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

ConditionalGenericConverter Spring 변환 API의 “궁극적 인 무기”이지만 일단 엔티티 변환 대부분을 처리하고 개발자 시간을 절약 할 수있게되면 구현됩니다. 엔티티 클래스를 컨트롤러의 매개 변수로 지정하고 구현에 대해 생각하지 않을 때 큰 도움이됩니다. 새로운 변환기 (물론 사용자 정의 및 비 엔티티 유형 제외).


답변

두 개의 변환기를 정적 내부 클래스로 구현하여 두 개의 개별 변환기 클래스를 사용해야 할 필요성을 해결할 수 있습니다.

public class FooConverter {
    public static class BarToBaz implements Converter<Bar, Baz> {
        @Override public Baz convert(Bar bar) { ... }
    }
    public static class BazToBar implements Converter<Baz, Bar> {
        @Override public Bar convert(Baz baz) { ... }
    }
}

여전히 두 파일을 개별적으로 등록해야하지만 최소한 변경하면 수정해야 할 파일 수가 줄어 듭니다.