본문 바로가기

Spring 정리ver2/Framework

[Spring Framework core] 3. Validation, Data Binding, and Type Conversion (3)

728x90

3.4. Spring Type Conversion

 

 

core.convert 패키지에서 일반적인 유형 변환 시스템을 제공한다.

시스템은 유형 변환 로직을 구현하기 위한 SPI와 런타임에 유형 변환을 수행하기 위한 API를 정의한다.

Spring 컨테이너 내에서 이 시스템을 구현의 대안으로 사용하여 PropertyEditor외부화된 빈 속성 값 문자열을 필수 속성 유형으로 변환할 수 있다. 유형 변환이 필요한 애플리케이션의 모든 위치에서 공용 API를 사용할 수 있다.

 

 

3.4.1. Converter SPI

SPI = Service Provider Interface 

https://www.baeldung.com/java-spi

 

 

유형 변환 논리를 구현하는 SPI는 다음 인터페이스 정의와 같이 단순하고 강력한 유형이다

package org.springframework.core.convert.converter;

public interface Converter<S, T> {

    T convert(S source);
}

 

고유한 변환기를 만들려면 Converter 인터페이스를 구현하고 S를 변환할 유형으로 매개변수화하고 T를 변환할 유형으로 매개변수화하면 된다. 또한 S의 컬렉션이나 배열을 T의 배열이나 컬렉션으로 변환해야 하는 경우 이러한 변환기를 투명하게 적용할 수 있다.

단, 위임하는 배열이나 컬렉션 변환기도 등록되어 있어야 한다(DefaultConversionService가 기본적으로 수행함).

 

convert(S)를 호출할 때마다 source 인수는 null이 아님을 보장해준다.

변환에 실패하면 변환기에서 확인되지 않은 예외가 발생할 수 있습니다.

특히 잘못된 소스 값을 보고하려면 IllegalArgumentException을 발생시켜야 한다.

변환기 구현이 스레드로부터 안전한지 확인해야한다.

여러 변환기 구현은 편의상 core.convert.support 패키지에 제공된다. 여기에는 문자열에서 숫자 및 기타 일반적인 유형으로의 변환기가 포함된다.

 

 

일반적인 Converter 구현인 StringToInteger 클래스

package org.springframework.core.convert.support;

final class StringToInteger implements Converter<String, Integer> {

    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}

 

 

 

 

3.4.2. Using ConverterFactory

전체 클래스 레이어 구조에 대한 변환 논리를 중앙 집중화 해야하는 경우에(String to Enum)
ConverterFactory를 구현할 수 있다
 
package org.springframework.core.convert.converter;

public interface ConverterFactory<S, R> {

    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
 
 
 

StringToEnumConverterFactory

package org.springframework.core.convert.support;

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }

    private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }

        public T convert(String source) {
            return (T) Enum.valueOf(this.enumType, source.trim());
        }
    }
}

 

 

 

3.4.3. Using GenericConverter

Sophisticated 한 변환 구현이 필요한 경우에는 GenericConverter interface를 사용한다

GenericConverter는 기존 Converter보다 유연하고 덜 강력한 유형의 signature 를 통해여러 소스 유형과 대상 유형 간의 변환을 지원한다. 또한 GenericConverter는 변환 논리를 구현할 때 사용할 수 있는 소스 및 대상 Field context를 제공한다

이러한 컨텍스트를 통해 유형 변환이 필드 주석 또는 필드 서명에 선언된 일반 정보에 의해 구동될 수 있다.

 

 

GenericConverter의 인터페이스 정의

package org.springframework.core.convert.converter;

public interface GenericConverter {

    public Set<ConvertiblePair> getConvertibleTypes();

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

GenericConverter를 구현하려면 getConvertibleTypes()가 지원되는 소스→대상 유형 쌍을 반환하도록 한다. 그리고 변환 논리를 포함하도록 convert(Object, TypeDescriptor, TypeDescriptor)를 구현한다. 소스 TypeDescriptor는 변환 중인 값을 보유하는 소스 필드에 대한 액세스를 제공한다.

 대상 TypeDescriptor는 변환된 값을 설정할 대상 필드에 대한 액세스를 제공한다.  GenericConverter의 좋은 예는 Java 배열과 컬렉션 간에 변환하는 converter이다. 이러한 ArrayToCollectionConverter는 대상 컬렉션 유형을 선언하는 필드를 검사하여 컬렉션의 요소 유형을 확인할 수 있다. 이렇게 하면 컬렉션이 대상 필드에 설정되기 전에 원본 배열의 각 요소를 컬렉션 요소 유형으로 변환할 수 있다.

 

**Because GenericConverter is a more complex SPI interface, you should use it only when you need it. Favor Converter or ConverterFactory for basic type conversion needs.

 

Using ConditionalGenericConverter

Sometimes, you want a Converter to run only if a specific condition holds true. For example, you might want to run a Converter only if a specific annotation is present on the target field, or you might want to run a Converter only if a specific method (such as a static valueOf method) is defined on the target class. ConditionalGenericConverter is the union of the GenericConverter and ConditionalConverter interfaces that lets you define such custom matching criteria:

public interface ConditionalConverter {

    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
 

ConditionalGenericConverter의 좋은 예는 영구 엔터티 식별자와 엔터티 참조 간에 변환하는 IdToEntityConverter이다.

이러한 IdToEntityConverter는 대상 엔터티 유형이 정적 파인더 메서드(예: findAccount(Long))를 선언하는 경우에만 일치할 수 있다. matches(TypeDescriptor, TypeDescriptor) 구현에서 이러한 파인더 메소드 검사를 수행한다.

 

 

 

 

3.4.4. The ConversionService API

ConversionService는 런타임에 유형 변환 논리를 실행하기 위한 통합 API를 정의한다. Convertor는 종종 다음 파사드 인터페이스 뒤에서 실행된다.
package org.springframework.core.convert;

public interface ConversionService {

    boolean canConvert(Class<?> sourceType, Class<?> targetType);

    <T> T convert(Object source, Class<T> targetType);

    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
 

대부분의 ConversionService구현은 또한 ConverterRegistry변환기를 등록하기 위한 SPI를 제공하는 를 구현하는데 내부적으로 ConversionService 구현은 등록된 변환기에 위임하여 형식 변환 논리를 수행한다.

 
강력한 ConversionService 구현은 core.convert.support 패키지에서 제공한다. GenericConversionService는 대부분의 환경에서 사용하기에 적합한 범용 구현이다. ConversionServiceFactory는 일반적인 ConversionService 구성을 만들기 위한 편리한 팩토리를 제공한다
 
 

3.4.5. Configuring a ConversionService

A ConversionService는 응용 프로그램 시작 시 인스턴스화되고 여러 스레드 간에 공유되도록 설계된 상태 비저장 개체로 Spring 애플리케이션에서는 일반적으로 ConversionService각 Spring 컨테이너(또는 ApplicationContext)에 대한 인스턴스를 구성한다. Spring은 이를 선택 ConversionService하고 프레임워크에서 유형 변환을 수행해야 할 때마다 사용한다. ConversionService이것을 빈에 주입하고 직접 호출할 수도 있다.

 Spring에 등록된 ConversionService가 없으면 기존 PropertyEditor 기반 시스템이 사용된다 @@

 

add the following bean definition with an id of conversionService:

<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean"/>
 

A default ConversionService can convert between strings, numbers, enums, collections, maps, and other common types. To supplement or override the default converters with your own custom converters, set the converters property. Property values can implement any of the Converter, ConverterFactory, or GenericConverter interfaces.

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>
 

It is also common to use a ConversionService within a Spring MVC application. See Conversion and Formatting in the Spring MVC chapter.

In certain situations, you may wish to apply formatting during conversion. See The FormatterRegistry SPI for details on using FormattingConversionServiceFactoryBean.

 

 

3.4.6. Using a ConversionService Programmatically

To work with a ConversionService instance programmatically, you can inject a reference to it like you would for any other bean. The following example shows how to do so:

프로그래밍 방식으로 인스턴스를 사용하려면 ConversionService다른 빈과 마찬가지로 인스턴스에 대한 참조를 주입하면 된다

 

@Service
public class MyService {

    public MyService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public void doIt() {
        this.conversionService.convert(...)
    }
}

 

대부분의 사용 사례에서 targetType을 지정하는 변환 메서드를 사용할 수 있지만 매개 변수가 있는 요소의 컬렉션과 같은 더 복잡한 유형에서는 작동하지 않는다.

프로그래밍 방식으로 정수 목록을 문자열 목록으로 변환하려는 경우 소스 및 대상 유형의 정식 정의를 제공해야한다

 

 

Fortunately, TypeDescriptor provides various options to make doing so straightforward, as the following example shows:

DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ...
cs.convert(input,
    TypeDescriptor.forObject(input), // List<Integer> type descriptor
    TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
 

Note that DefaultConversionService automatically registers converters that are appropriate for most environments. This includes collection converters, scalar converters, and basic Object-to-String converters. You can register the same converters with any ConverterRegistry by using the static addDefaultConverters method on the DefaultConversionService class.

Converters for value types are reused for arrays and collections, so there is no need to create a specific converter to convert from a Collection of S to a Collection of T, assuming that standard collection handling is appropriate.

 

728x90