org.springframework.validation.DataBinder
기술적인 관점 : 프로퍼티 값을 타켓 객체에 설정하는 기능
사용자 관점 : 사용자 입력값을 애플리케이션 도메인 모델에 동적으로 변환해 넣어주는 기능
해석하면 : 입력값은 대부분 "문자열"인데, 그 값을 객체가 가지고 있는 프로퍼티 타입 (즉, int, long, boolean, Date 등등 ) 심지어, Event,Book 같은 도메인 타입으로도 변환해서 넣어주는 기능.
PropertyEditor
- 스프링 3.0 이전까지 DataBinder가 변환 작업 사용하던 인터페이스
- 쓰레드 - 세이프 하지 않음 (상태 정보 저장 하고 있음, 따라서 싱글톤 빈으로 등록해서 사용하다가는...)
- Object와 String 간의 변환만 할 수 있어, 사용 범위가 제한적임. (그래도 그런 경우가 대부분이기 때문에 잘 사용해왔음. 조심해서...)
public class EventPropertyEditor extends PropertyEditorSupport{
@Override
public String getAsText(){
return ((Event)getValue()).getTitle();
}
@Override
public void setAsText(String text) throws IllegalArgumentException{
int id = Integer.parseInt(text);
Event event = new Event();
event.setId(id);
setValue(event);
}
}
- @RestController : @Controller + @ResponseBody 의 축약형으로써, 리턴값을 뷰리졸버로 매핑하지 않고 그대로 출력해준다.
- @GetMapping : @RequestMapping(method = RequestMethod.GET) 의 축약형으로써, 애노테이션만 보고 무슨 메소드 요청인지 바로 알아볼 수 있다.
고전적인 EventPropertyEditor 사용법
public class Event {
private Integer id;
private String title;
public Event(Integer id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "Event{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
@RestController
public class EventController {
@InitBinder//Controller에 propertyEditor를 등록해준다.
public void init(WebDataBinder webDataBinder){
webDataBinder.registerCustomEditor(Event.class, new EventEditor());
}
@GetMapping("/event/{event}") //event1, event2 이런식으로 요청이 들어오기 때문에
public String getEvent(@PathVariable Event event){//spring이 도메인 타입으로 변환해주면 @PathVariable로 Event를 도메인으로 받는다.
System.out.println(event);
return event.getId().toString();
}
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class EventControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void getText() throws Exception {
mockMvc.perform(get("/event/1"))
.andExpect(status().isOk())
.andExpect(content().string("1"));
}
}
2020-07-12 15:06:44.256 WARN 6205 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'com.example.demospring51.Event'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.demospring51.Event': no matching editors or conversion strategy found]
에러 코드를 보면, Failed to convert value of type 타입 변환을 실패했다.
마지막을 보면, no matching editors or conversion strategy found -> property editor 를 만들어주면 된다.
public class EventEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Event event = (Event)getValue();
return event.getId().toString();
}
//우리에게 필요한 것은 text를 event로 변환하는것.
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Event(Integer.parseInt(text)));
//상태 정보를 저장하고있어서, 여러 쓰레드에서 공유해서 사용하면 안된다.
//(즉, PropertyEditor의 구현체들은 빈으로 등록해서 사용하면 큰일난다.)
//그래서 Controller에 @InitBinder로 등록을 해준다
}
}
EventController에 PropertyEditor를 등록해준다. 아래 코드를 추가해준다.
( Controller에 등록! )
@InitBinder//propertyEditor를 등록하는 방법
public void init(WebDataBinder webDataBinder){
webDataBinder.registerCustomEditor(Event.class, new EventEditor());
}
등록해주고 다시 실행을 해주면?
Event{id=1, title='null'}
정상적으로 실행이 되었다.
어떻게 실행이 된것인가?
controller가 어떤 요청을 처리하기전에
이 controller에서 정의한 DataBinder(WebDataBinder)에 들어있는 PropertyEditor(registerCustomEditor)를 사용하게 된다.
@InitBinder//propertyEditor를 등록하는 방법
public void init(WebDataBinder webDataBinder){
webDataBinder.registerCustomEditor(Event.class, new EventEditor());
}
그래서 이 PropertyEditor를 사용했기 때문에..
문자열로 들어온 “1”을 숫자로 변환해서 event객체로 바꾸는 일이 -> setAsText() 메소드가 실행이 된것이다.
public class EventEditor extends PropertyEditorSupport{
...
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Event(Integer.parseInt(text)));
//text를 Integer로 변환시켜주고 Event 생성자에 넘겨주고 있다.
}
}
출처: https://countryxide.tistory.com/45 [배워서 남주자]