Spring의 Resource와 Validation의 추상화


Resource의 추상화


org.springframework.core.io.Resource

java.net.URL 클래스를 감싸 Low Level에 있는 Resource에 접근하능 기능을 만들었다.


추상화 한 이유

  • 클래스패스 기준으로 리소스 읽어오는 기능 부재
  • ServletContext를 기준으로 상대 경로로 읽어오는 기능 부재
  • 새로운 핸들러를 등록하여 특별한 URL 접미사를 만들어 사용할 수는 있지만 구현이 복잡하고 편의성 메소드가 부족하다.


주요 메소드

  • getInputStream()
  • exitst()
  • isOpen()
  • getDescription(): 전체 경로 포함한 파일 이름 또는 실제 URL


구현체

  • UrlResource: java.net.URL 참고, 기본으로 지원하는 프로토콜 http, https, ftp, file, jar.
  • ClassPathResource: 지원하는 접두어 classpath: (ClassPath 기준)
  • FileSystemResource (FileSystem 기준)
  • ServletContextResource: 웹 애플리케이션 루트에서 상대 경로로 리소스 찾는다.


리소스 읽어오기

  • Resource의 타입은 locaion 문자열과 ApplicationContext의 타입에 따라 결정 된다.
    • ClassPathXmlApplicationContext -> ClassPathResource
    • FileSystemXmlApplicationContext -> FileSystemResource
    • WebApplicationContext -> ServletContextResource
  • ApplicationContext의 타입에 상관없이 리소스 타입을 강제하려면 java.net.URL 접두어(+ classpath:)중 하나를 사용할 수 있다.
    • classpath:me/whiteship/config.xml -> ClassPathResource
    • file:///some/resource/path/config.xml -> FileSystemResource

접두어를 사용한다면 어디에서 사용하더라도 명시적으로 확인이 가능하다. 따라서 이 방법을 추천하고 싶다.



Validation 추상화


org.springframework.validation.Validator

  • 애플리케이션에서 사용하는 객체 검증용 인터페이스


특징

  • 어떤한 계층과도 관계가 없다. => 모든 계층(웹, 서비스, 데이터)에서 사용해도 좋다.
  • 구현체 중 하나로, JSR-303(Bean Validation 1.0)과 JSR-349(Bean Validation 1.1)을 지원한다. (LocalValidatorFactoryBean)
  • DataBinder에 들어가 바인딩 할 때 같이 사용되기도 한다.


스프링 부트 2.0.5 이상 버전을 사용할 때

  • LocalValidatorFactoryBean는 빈으로 자동 등록된다.
  • JSR-380(Bean Validation 2.0.1) 구현체로 hibernate-validator 사용


Validator 사용


1. Validator를 만들어서 사용

Validation 대상 Class 생성

Event.java

@Component
public class Event {

    Integer id;
    String title;

}
  • Getter, Setter는 생략함


Validator 생성

EventValidator.java

public class EventValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass) {
        return Event.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "nonempty", "Empty title is not allowed");
        // rejectIfEmptyOrWhitespace(errors, field, errorCode, defaultMessage)
        // errorCode = message resource에서 실제 메세지를 가져오기 위한 Key
        // defaultMessage = errorCode로 사용할 메세지를 찾지 못하였을때 사용할 메세지
    }

}


Runner에서 검증

AppRunner.java

@Component
public class AppRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

       Event event = new Event();
       EventValidator eventValidator = new EventValidator();
       Errors errors = new BeanPropertyBindingResult(event, "event");
       // 실제로 Errors라는 interface는 자주 보겠지만 BeanPropertyBindingResult과 같은 구현체는
       // Spring mvc가 자동으로 생성해서 parameter에 전달해 줄 것이기 때문에 직접 사용할일은 없을 것이다.

       eventValidator.validate(event, errors);

       System.out.println(errors.hasErrors());

       errors.getAllErrors().forEach( e -> {
           System.out.println("===== error code =====");
           Arrays.stream(e.getCodes()).forEach(System.out::println);
           System.out.println(e.getDefaultMessage());
       });

    }

}


출력 결과

true
===== error code =====
nonempty.event.title
nonempty.title
nonempty.java.lang.String
nonempty
Empty title is not allowed


2. LocalValidatorFactoryBean를 사용하여 Annotation을 통한 Validation

  • 위에서 설명하였듯 스프링 부트 2.0.5 이상 버전을 사용할 때 LocalValidatorFactoryBean는 빈으로 자동 등록된다.

검증 대상 Class에 Annotation 작성

Event.java

public class Event {

    Integer id;
    String title;

    @NotNull @Min(0)
    Integer limit;

    @Email
    String email;

}
  • Getter, Setter는 생략


Runner에서 검증

AppRunner.java

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    Validator validator;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        System.out.println(validator.getClass());

        Event event = new Event();
        event.setLimit(-1);
        event.setEmail("seongmun");
        Errors errors = new BeanPropertyBindingResult(event, "event");

        validator.validate(event, errors);

        System.out.println(errors.hasErrors());

        errors.getAllErrors().forEach( e -> {
            System.out.println("===== error code =====");
            Arrays.stream(e.getCodes()).forEach(System.out::println);
            System.out.println(e.getDefaultMessage());
        });

    }

}


출력 결과

class org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
true
===== error code =====
Min.event.limit
Min.limit
Min.java.lang.Integer
Min
반드시 0보다 같거나 커야 합니다.
===== error code =====
Email.event.email
Email.email
Email.java.lang.String
Email
이메일 주소가 유효하지 않습니다.


정리

  • 복잡한 로직의 검증에는 validator를 만들어서 사용할 수 있으며 간단한 검증은 LocalValidatorFactoryBean를 사용할 수 있다.


Project Repository

  • https://github.com/Seongmun-Hong/SpringStudy


Reference

  • https://www.inflearn.com/course/spring-framework_core
Share :

Comments