1. 개요
이 기사는 객체 변환에 자동 프리미티브를 적용하여 코드를 더 명확하고 읽기 쉽게 만들기 위해 Spring의 데이터 바인딩 메커니즘을 사용하는 방법을 보여줍니다.
기본적으로 Spring은 단순한 유형을 변환하는 방법 만 알고 있습니다. 즉, 데이터를 컨트롤러 Int , String 또는 Boolean 유형의 데이터에 제출 하면 적절한 Java 유형에 자동으로 바인딩됩니다.
그러나 실제 프로젝트에서는 더 복잡한 유형의 객체를 바인딩해야 할 수 있으므로 충분하지 않습니다 .
2. 요청 매개 변수에 개별 개체 바인딩
간단하게 시작하고 먼저 간단한 유형을 바인딩 해 보겠습니다. Converter <S, T> 인터페이스 의 사용자 지정 구현을 제공해야합니다. 여기서 S 는 변환 대상 형식이고 T 는 변환 대상 형식입니다.
@Component
public class StringToLocalDateTimeConverter
implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(
source, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
이제 컨트롤러에서 다음 구문을 사용할 수 있습니다.
@GetMapping("/findbydate/{date}")
public GenericEntity findByDate(@PathVariable("date") LocalDateTime date) {
return ...;
}
2.1. 열거 형을 요청 매개 변수로 사용
다음으로 e num 을 RequestParameter 로 사용하는 방법을 살펴 보겠습니다 .
여기에 간단한 enum 모드가 있습니다 .
public enum Modes {
ALPHA, BETA;
}
다음과 같이 열거 형 변환기에 문자열 을 빌드 합니다.
public class StringToEnumConverter implements Converter<String, Modes> {
@Override
public Modes convert(String from) {
return Modes.valueOf(from);
}
}
그런 다음 Converter 를 등록해야합니다 .
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToEnumConverter());
}
}
이제 Enum 을 RequestParameter 로 사용할 수 있습니다 .
@GetMapping
public ResponseEntity<Object> getStringToMode(@RequestParam("mode") Modes mode) {
// ...
}
또는 PathVariable로 :
@GetMapping("/entity/findbymode/{mode}")
public GenericEntity findByEnum(@PathVariable("mode") Modes mode) {
// ...
}
3. 개체 계층 바인딩
때때로 우리는 개체 계층 구조의 전체 트리를 변환해야하며 개별 변환기 세트보다 중앙 집중식 바인딩을 갖는 것이 합리적입니다.
이 예에서는 AbstractEntity 기본 클래스가 있습니다.
public abstract class AbstractEntity {
long id;
public AbstractEntity(long id){
this.id = id;
}
}
그리고 하위 클래스 Foo 와 Bar :
public class Foo extends AbstractEntity {
private String name;
// standard constructors, getters, setters
}
public class Bar extends AbstractEntity {
private int value;
// standard constructors, getters, setters
}
이 경우 ConverterFactory <S, R>을 구현할 수 있습니다. 여기서 S는 변환 대상 유형이고 R은 변환 할 수있는 클래스 범위를 정의하는 기본 유형입니다 .
public class StringToAbstractEntityConverterFactory
implements ConverterFactory<String, AbstractEntity>{
@Override
public <T extends AbstractEntity> Converter<String, T> getConverter(Class<T> targetClass) {
return new StringToAbstractEntityConverter<>(targetClass);
}
private static class StringToAbstractEntityConverter<T extends AbstractEntity>
implements Converter<String, T> {
private Class<T> targetClass;
public StringToAbstractEntityConverter(Class<T> targetClass) {
this.targetClass = targetClass;
}
@Override
public T convert(String source) {
long id = Long.parseLong(source);
if(this.targetClass == Foo.class) {
return (T) new Foo(id);
}
else if(this.targetClass == Bar.class) {
return (T) new Bar(id);
} else {
return null;
}
}
}
}
보시다시피 구현해야하는 유일한 메소드는 필요한 유형에 대한 변환기를 반환하는 getConverter () 입니다. 그런 다음 변환 프로세스가이 변환기에 위임됩니다.
그런 다음 ConverterFactory 를 등록해야합니다 .
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new StringToAbstractEntityConverterFactory());
}
}
마지막으로 컨트롤러에서 원하는대로 사용할 수 있습니다.
@RestController
@RequestMapping("/string-to-abstract")
public class AbstractEntityController {
@GetMapping("/foo/{foo}")
public ResponseEntity<Object> getStringToFoo(@PathVariable Foo foo) {
return ResponseEntity.ok(foo);
}
@GetMapping("/bar/{bar}")
public ResponseEntity<Object> getStringToBar(@PathVariable Bar bar) {
return ResponseEntity.ok(bar);
}
}
4. 바인딩 도메인 개체
데이터를 객체에 바인딩하려는 경우가 있지만 간접적 인 방식 (예 : Session , Header 또는 Cookie 변수)으로 제공되거나 데이터 소스에 저장되는 경우도 있습니다. 이 경우 다른 솔루션을 사용해야합니다.
4.1. 사용자 지정 인수 확인자
우선 이러한 매개 변수에 대한 어노테이션을 정의합니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Version {
}
그런 다음 사용자 지정 HandlerMethodArgumentResolver를 구현합니다 .
public class HeaderVersionArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(Version.class) != null;
}
@Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request
= (HttpServletRequest) nativeWebRequest.getNativeRequest();
return request.getHeader("Version");
}
}
마지막으로 Spring에 검색 할 위치를 알려줍니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
//...
@Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new HeaderVersionArgumentResolver());
}
}
그게 다야. 이제 컨트롤러에서 사용할 수 있습니다.
@GetMapping("/entity/{id}")
public ResponseEntity findByVersion(
@PathVariable Long id, @Version String version) {
return ...;
}
보시다시피 HandlerMethodArgumentResolver 의 resolveArgument () 메서드는 Object를 반환합니다 . 즉, String 뿐만 아니라 모든 객체를 반환 할 수 있습니다.
5. 결론
결과적으로 우리는 많은 일상적인 변환을 제거하고 Spring이 우리를 위해 대부분의 작업을 수행하도록했습니다. 마지막으로 결론을 내립니다.
- 개별 단순 유형에서 객체로 변환하려면 Converter 구현을 사용해야합니다.
- 다양한 객체에 대한 변환 논리를 캡슐화하기 위해 ConverterFactory 구현을 시도 할 수 있습니다.
- 데이터가 간접적으로 제공되거나 관련 데이터를 검색하기 위해 추가 논리를 적용해야하는 경우 HandlerMethodArgumentResolver 를 사용하는 것이 좋습니다.
평소와 같이 모든 예제는 항상 GitHub 저장소 에서 찾을 수 있습니다 .
- https://docs.spring.io/spring-framework/docs/current/reference/html
- https://www.baeldung.com/spring-mvc-custom-data-binder
'Java' 카테고리의 다른 글
Java Generics – <?> 대 <? 개체 확장> (0) | 2021.04.16 |
---|---|
Spring REST 컨트롤러에서 HTTP 헤더를 읽는 방법 (0) | 2021.04.15 |
Spring 컨트롤러에서 List 유효성 검사 (0) | 2021.04.15 |
스프링 유효성 검사 메시지 보간 (0) | 2021.04.15 |
Spring URL에서 슬래시 문자 사용 (0) | 2021.04.15 |