스프링 프레임워크 Resource, Validation, 데이터 바인딩, SpEL
지난 강의 정리
ApplicationContext는 단순히 BeanFactory의 기능만 하는 것이 아니고 ResourceLoader, ApplicationEventPublisher,MessageSource, Environment 등 여러 기능들이 있습니다.
Resource 추상화
특징
- java.net.URL을 추상화 한 것
- 스프링 내부에서 많이 사용하는 인터페이스
추상화 한 이유
- 클래스패스 기준으로 리소스 읽어오는 기능 부재
- ServletContext를 기준으로 상대 경로로 읽어오는 기능 부재
- 새로운 핸들러를 등록하여 특별한 URL 접미사를 만들어 사용할 수는 있지만 구현이 복잡하고 편의성 메소드가 부족함
인터페이스
- 상속 받은 인터페이스
- 주요 메소드
- getInputStream()
- exists();
- isOpen();
- getDescription(); 전체 경로 포함한 파일 이름 또는 실제 URL
구현체
- UrlResource: http, https, ftp, file, jar
- ClassPathResource: 접두어 classpath:
- FileSystemResource
- ServletContextResource: 웹 애플리케이션 루트에서 상대 경로로 리소스를 찾는다.
리소스 읽어오기
-
Resource의 타입은 locaion 문자열과 ApplicationContext의 타입에 따라 결정 된다.
-
ClassPathXmlApplicationContext -> ClassPathResource
-
FileSystemXmlApplicationContext -> FileSystemResource
-
WebApplicationContext -> ServletContextResource
리소스 이름만 적으면 ServletConntextResource로 리저빙이 된다.
-
-
**ApplicationContext의 타입에 상관없이 리소스 타입을 강제하려면 **java.net.URL 접두어(+ classpath:) 중 하나를 사용할 수 있다.
- classpath:me/whiteship/config.xml -> ClassPathResource
- file:///some/resource/path/config.xml -> FileSystemResource
Validation 추상화
org.sprignframework.validation.Validator
어플리케이션에서 사용하는 객체를 검증하는 인터페이스.
스프링 부트 2.0.5 이상 버전을 사용할 때 스프링에서 빈으로 등록되어 있습니다.
- LocalValidationFactoryBean
스프링 부트 2.3 이상 버전에서는 validator 의존성이 포함이 되어있지 않습니다. 의존성을 추가 해야 사용이 가능하도록 바뀌었습니다.
도메인 객체에 @NotNull, @Min, @Max 등의 어노테이션을 붙이고 Validator를 사용하면 error를 잡아 냅니다.
검증 방법
검증할 도메인 객체
public class Event {
@Min(0)
Integer id;
@NotNull
String title;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
검증
@Component
public class AppRunner implements ApplicationRunner{
@Autowired
Validator validator;
@Override
public void run(ApplicationArguments args) throws Exception {
Event event = new Event();
Errors errors = new BeanPropertyBindingResult(event, "event");
validator.validate(event, errors);
System.out.println(errors.hasErrors());
errors.getAllErrors().forEach(e -> {
System.out.println("=== error code ===");
System.out.println(Arrays.toString(e.getCodes()));
});
}
}
데이터 바인딩 추상화: PropertyEditor
DataBinder
기술적인 관점: 프로퍼티 값을 타겟 객체에 설정하는 기능
사용자 관점: 사용자 입력값을 애플리케이션 도메인 모델에 동적으로 변환해 넣어주는 기능
입력값은 대부분 문자열인데 그 값을 객체가 가지고 있는 int, long, boolean, date 등 Event, Book 같은 도메인 타입으로 변환해서 넣어주는 기능
PropertyEditor
- 스프링 3.0 이전까지 DataBinder가 변환 작업을 사용하던 인터페이스
- 쓰레드 세이프하지가 않습니다. ( 상태 정보를 저장하고 있어 싱글톤 빈으로 등록해서 사용하면 안됨)
- Object와 String 간의 변환만 가능하여 제한적입니다.
스트링으로 들어온 것을 Event로 변환하는 과정입니다.
public class EventEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Event event = (Event)getValue();
return event.getId().toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Event(Integer.parseInt(text)));
}
}
setValue는 상태정보를 가지고 있어 쓰레드 세이프티 하지 않으므로 빈으로 등록하여 사용하면 안됩니다. 다른 쓰레드와 공유해서 잘 못사용될 가능성이 있습니다. 위의 방식은 사용하기 번거로우며 스트링과 오브젝트간의 변환만 가능합니다.
@RestController
public class EventController {
@InitBinder
public void init(WebDataBinder webDataBinder){
webDataBinder.registerCustomEditor(Event.class , new EventEditor());
}
@GetMapping("/event/{event}")
public String getEvent(@PathVariable Event event) {
System.out.println(event);
return event.getId().toString();
}
}
controller에서 InitBinder를 통해서 사용하는 방법을 추천합니다.
데이터 바인딩 추상화: Convertor와 Fomatter
public class EventConverter {
public static class StringToEventConverter implements Converter<String, Event>{
@Override
public Event convert(String source) {
return new Event(Integer.parseInt(source));
}
}
public static class EventToStringConverter implements Converter<Event, String>{
@Override
public String convert(Event source) {
return source.getId().toString();
}
}
}
Converter
- S타입을 T 타입으로 변환할 수 있는 매우 일반적인 변환기
- 상태 정보가 없으므로 쓰레드 세이프 합니다.
- ConverterRegistry에 등록해 사용
implements Converter<S,T>
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new EventConverter.StringToEventConverter());
}
}
WebConfig 환경 설정으로 registry 하여 사용합니다.
기본적인 Integer 등 컨버터로 등록되어 있으므로 모든 것을 만들지 않고 필요에 따라 만들어 사용합니다.
Formatter
- PropertyEditor의 대체
- Object 와 String간 변환을 담당
- 문자열을 Locale에 따라 다국화하는 기능(optional)
- 웹 설정으로 포매터 등록후 사용
public class EventFormatter implements Formatter<Event> {
@Override
public Event parse(String text, Locale locale) throws ParseException {
return new Event(Integer.parseInt(text));
}
@Override
public String print(Event object, Locale locale) {
return object.getId().toString();
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new EventFormatter());
}
}
ConversionService
DefaultFormattingConversionService
실제 변환 작업을 이 인터페이스를 통해서 쓰레드-세이프하게 사용할 수 있습니다.
스프링 MVC, 빈 설정, SpEL에서 사용
스프링 부트에서는 WebConversionService가 @Component로 Converter, Formatter를 빈으로 등록을 하면 웹 설정을 하지 않더라도 자동으로 등록을 해줍니다.
Fomatter 사용을 추천…
SpEL (스프링 Expression Language)
스프링 EL이란?
- 객체 그래프를 조회하고 조작하는 기능을 제공
- Unified EL과 비슷하지만, 메소드 호출 지원, 문자열 템플릿 기능 제공
- 스프링 프로젝트 전반에 걸쳐 사용할 수 있는 EL
문법
- #{“표현식”}
- ${“프로퍼티”}
- #{${프로퍼티} + 1} 가능 / 반대는 X
사용하는 곳
- @Value
- @ConditionalOnExpression
- 스프링 시큐리티
- 스프링 데이터 @Query
- Thymeleaf