PropertyEditorSupport 살펴보기 3

참조 : 자바빈 Property Editors

Petclinic의 petForm.jsp 파일을 보면 다음과 같은 코드가 있습니다.

    <tr>
      <th>
        Type: <form:errors path=”type” cssClass=”errors”/>
        <br/>
        <form:select path=”type” items=”${types}”/>
      </th>
    </tr>

여기서 굵은 글씨에 주목해야 합니다. type은 문자열 타입이 아닙니다. 스프링이 기본으로 제공해주는 PropertyEditor들이 primitive 타입들과 그것의 배열들은 알아서 바인딩 해주지만, 저 타입은 스프링이 알지 못합니다. 따라서 자동으로 바인딩이 되지 않을 겁니다. 따라서 저 것은 그냥 객체입니다.

그런데, 문자열이 올 자리에 객체가 와서 그런건지, toString()이 호출 됩니다. 그리고 PetType 클래스에는 toString을 다음과 같이 구현해 뒀습니다.

    public String toString() {
        return this.getName();
    }

오호.. 그럼 저 자리에 문자열이 채워지고 따라서 getAsString() 메소드를 구현할 필요가 없어졌습니다. 그래서 그런지 PetTypeEditor 클래스에는 정말 getAsString()이 없습니다.

public class PetTypeEditor extends PropertyEditorSupport {

    private final Clinic clinic;

    public PetTypeEditor(Clinic clinic) {
        this.clinic = clinic;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        for (PetType type : this.clinic.getPetTypes()) {
            if (type.getName().equals(text)) {
                setValue(type);
            }
        }
    }

}

하지만 어디까지나 저런 경우는 특수한 경우일테니까요.(PetType에 속성이 name밖에 없더군요.) 대부분의 경우에는 getAsText()를 구현해서 써야겠죠.

public String getAsText() {
  Calendar value = (Calendar) getValue();
  return (value != null ?
    this.dateFormat.format(value.getTime()) :
    “”
  );
}

위 코드는 여기서 참조했습니다. getValue()를 사용해서 value 객체를 가져온 다음 이 객체가 가지고 있는 속성을 사용해서 문자열(String)을 만들어서 반환하고 있습니다.

이렇게 만들어 둔 Propery Editor를 스프링이 사용하도록 등록하는 방법이 있습니다. 여러 군대에서 사용되는 Property Editor라면

    <bean name=”customEditorConfigurer” class=”org.springframework.beans.factory.config.CustomEditorConfigurer”>
        <property name=”customEditors”>
            <map>
                <entry key=”java.util.regex.Pattern”>
                    <bean class=”PatternPropertyEditor”/>
                </entry>
            </map>
        </property>
    </bean>

이렇게 스프링 설정 파일에 있는 <property>의 value 값에 적용됩니다.

컨트롤러에서 사용할 거라면… 폼 컨트롤러 안에 다음과 같은 코드를 추가합니다.

protected void initBinder(HttpServletRequest request,
    ServletRequestDataBinder binder)
    throws Exception {
    binder.registerCustomEditor(
      Calendar.class,
      new CustomCalendarEditor(
        new SimpleDateFormat(“dd/MM/yyyy”), true));
  }

Spring 2.5에서는 다음과 같이 추가합니다.

@InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd”);
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(
                dateFormat, false));
    }

음… 이제 JSP의 EL에서 member.department.departmentid 이렇게 길게 쓰지 않아도 되겠습니다. 그냥 member.department 라고 써도 커스텀 에디터가 ember.department.departmentid 이렇게 쓴거나 동일하게 처리해 줄 것 입니다.

PropertyEditorSupport 살펴보기 2

이전 글에 이어서 먼저 setValue(Object) 메소드를 보겠습니다. 이 녀석이 뭐하는 녀석인지… API에 써있지만 별로 와닿지 않습니다.

그래서 그냥 소스코들 봤습니다. 즉 PropertyEditor 인터페이스의 구현체인 PropertyEditorSupport 클래스를 살펴봤습니다.

    public void setValue(Object value) {
    this.value = value;
    firePropertyChange();
    }

위와같이 구현되어 있습니다. value라는 이름으로 객체를 받아와서 ‘속성 바꿔치기’를 하고 있군요. 저 메소드도 보이지만, 먼 산으로 갈까봐 관 둡니다. 이런.. 소스를 봐도 별 소득이 없네요. 예제 코드를 봐야겠습니다. 스프링의 Petclinic을 뒤져보면 다음과 같은 코드가 나옵니다.

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        for (PetType type : this.clinic.getPetTypes()) {
            if (type.getName().equals(text)) {
                setValue(type);
            }
        }
    }

setAsText에서 setValue()를 호출하고 있군요. 이 때 넘겨주는 객체는 PetType이라는 객체인 걸 보니, 이 프로퍼티 에디터는 PetType을 다루기 위해 작성된 것 같습니다. 잠깐 딴 길로 새서.. setAsText()를 보겠습니다.

    public void setAsText(String text) throws java.lang.IllegalArgumentException {
    if (value instanceof String) {
        setValue(text);
        return;
    }
    throw new java.lang.IllegalArgumentException(text);
    }

이 메소드는 String을 받아온 다음에 객체를 만들어서 setValue() 메소드에 넘겨주고 있습니다.

이제 조금 감이 잡힙니다.
사용자 삽입 이미지어디에선가 입력된 텍스트를 가지고 객체를 만들어서 setValue() 메소드에 그 객체를 넘겨주는 메소드인가 봅니다. 그리고 setValue()는 아마도 해당 객체를 어딘가에서 값으로 사용하도록 넘겨줄 겁니다.

이제 String getAsText() 하나만 남아있군요. 이 메소드는 setAsText(String)과 반대일 것 같다는 느낌이 팍팍 듭니다. 다음과 같이 구현되어 있습니다.

    public String getAsText() {
    if (value instanceof String) {
        return (String)value;
    }
    return (“” + value);
    }

흠.. value 객체를 이번에는 거꾸로 String 타입으로 변환해서 반환하는 메소드입니다. PetType으로 예를 들자면,

    @Override
    public String getAsText() {
        Object value = getValue();
        if(value instanceof PetType)
            return ((PetType)value).getName();
        else
            throw new RuntimeException();
    }

이런식으로 구현할 수도 있겠습니다. PetType 객체가 오면 이 객체가 가지고 있는 name 속성이 이 객체를 대변하도록 말이죠.
사용자 삽입 이미지
그럼 대체 객체의 어떤 값이 해당 객체를 대신하도록 하는것이 좋을까요? 당연히 유일한 값이 좋겠죠. 흠..

다음에는 이런 PropertyEditor를 사용한 PetClinic 예제를 살펴보겠습니다.

PropertyEditorSupport 살펴보기 1

이 클래스는 JDK 1.5에 추가된 클래스 입니다. 이 클래스를 좀 살펴보겠습니다. 이 클래스는 PropertyEditor 인터페이스를 구현하고 있습니다. 아마도 PropertyEditor를 쌩으로 구현하기는 불편하니까 Custom Editor를 구현하기 편하게 만들어둔 클래스로 유추 됩니다.
사용자 삽입 이미지

PropertyEditor 인터페이스를 보겠습니다.

사용자 삽입 이미지

API 대강 번역

PeopertyEditor 클래스는 GUI에 사용자가 주어진 타입의 속성 값을 편집하고 싶을 때 이를 지원하기 위해 제공된다

PropertyEditor는 속성 값을 보여주거나 수정할 수 있는 다양한 방법을 제공한다. 대부분의 PropertyEditor는 본 API 문서에서 가용한 옵션 중에 일부만 사용해도 충분할 것이다.

간단한 PropertyEditor들은 getAsTest와 setAsText 메소드만 사용할 것이고 복잡한 타입일 경우에는 paintValue와 getCustomEditor를 사용할 것이다.

모든 PropertyEditor는 반드시 다음의 세 가지 방법 중에 한 가지 스타일로 속성을 보여주어야 한다.
1. isPaintable
2. getTags()에서 null이 아닌 String[]을 반환하고 getAsText()에서 null이 아닌 값을 반환한다.
3. getAsText()에서 null이 아닌 값을 반환한다.

모든 Property Editor들은 반드시 setValue메소드에 이 PropertyEditor를 적용할 객체를 넘겨주어야 한다. 또한 반드시 custom editor를 지원하거나 setAsText를 지원해야 한다.

각각의 PropertyEditor들은 기본 생성자를 가지고 있어야 한다.

복잡한것 같지만 대강 요약하면 다음과 같습니다.
1. 기본 생성자가 있어야 한다.
2. setValue(Object) 메소드에 넘겨줄 Object 객체는 해당 PropertyEditor가 처리할 객체여야 한다.
3. getAsText()를 구현하거나 isPaintable()을 구현해야 한다.
4. setAsText()를 구현해야 한다.

그럼 다음에는 저 많은 메소드들 중에서 딱 저 세 개의 메소드만(isPaintable()은 생략) 살펴보겠습니다.