오늘 이야기 할 내용은 Spring과 Spring boot의 새로운 클래스에 대해서 알아보려고 한다.  제목은 Spring 이라고 했지만 Spring boot 도 포함되어 있다. 대략 4가지 정도 클래스를 알아볼 예정인데 각 설명마다 프로젝트와 버전을 명시하겠다. 

보다 많은 클래스가 추가 되었지만 다 알아 볼 수도 없고 또 한 클래스들이 디펜더시도 있어 유틸성의 클래스들만 알아보도록 하자.

DataSize

DataSize 클래스는 Spring 5.1에 포함될 예정이다. 아직 릴리즈는 되지 않았지만 util 클래스라 크게 바뀌지 않을 듯으로 보인다. 한번 알아보도록 하자.

DataSize dataSize = DataSize.of(10, DataUnit.TERABYTES);
System.out.println(dataSize);
System.out.println(dataSize.toGigaBytes());

아주 간단하다. 특정한 DataSize를 작성하면 그에 맞게 원하는 데이터 형식으로 바꿔준다.  위의 코드는 10테라 바이트를  기가 바이트로 바꾸어 리턴해주는 코드이다. 실제로 내부적으로는  long 형태의 byte 로 저장하고 있다. 지금 현제 지원해주는 단위는 BYTES, KILOBYTES, MEGABYTES, GIGABYTES, TERABYTES 를 지원해주 고 있다. 뭐 아직까지는 테라바이트 이상으로는 필요 없을 듯으로 판단하여 그런듯 하다. 

위의 예제는 of 메서드를 이용해서 생성했지만 굳이 그럴 필요 없이 ofXXX 메서드를 이용해서 사용하면 더욱 편리하다.

DataSize bytes = DataSize.ofBytes(100);
DataSize kiloBytes = DataSize.ofKiloBytes(100);
DataSize megaBytes = DataSize.ofMegaBytes(100);
DataSize gigaBytes = DataSize.ofGigaBytes(100);
DataSize teraBytes = DataSize.ofTeraBytes(100);

좀 더 간편하게 사용할 수 있을 듯하다. 이것은 Spring에서 지원해주는 클래스지만 Spring boot 2.1에서도 직접적으로 사용할 수 있다.

@ConfigurationProperties("foo")
public class DataSizeProperties {

    private DataSize tempSize;

    public DataSize getTempSize() {
        return tempSize;
    }

    public void setTempSize(DataSize tempSize) {
        this.tempSize = tempSize;
    }
}

우리가 자주 사용하는 @ConfigurationProperties 어노테이션에서도  DataSize 클래스를 사용해서 매핑할 수 있다. 

사용법은 아래와 같이 간단하다.

foo.temp-size=10
foo.temp-size=10MB
foo.temp-size=10GB
...

기본적으로 아무 단위가 없다면 Byte로 설정된다. 만약 원하는 단위가 있다면 위 처럼 해당 단위를 작성해주면 된다. 하지만 여기서 주의할 점은 모두 대문자를 이용해야 한다는 것이다.  왜소문자는 파싱을 안되게 했을까? 흠흠 Enum 타입에 있는 문자로 결정하는 것 같은데 자세히는 살펴보지 않았다. 아무튼 그렇다. 

만약 해당 타입을 기본적인 단위로 지정해주고 싶다면 다음과 같이작성해도 무방하다.

@ConfigurationProperties("foo")
public class DataSizeProperties {

    @DataSizeUnit(DataUnit.GIGABYTES)
    private DataSize tempSize;

    public DataSize getTempSize() {
        return tempSize;
    }

    public void setTempSize(DataSize tempSize) {
        this.tempSize = tempSize;
    }
}

위와 같이 @DataSizeUnit 어노테이션을 이용해서 해당 단위를 기본적으로 설정 할 수 있다. 그러면 프로퍼티에 굳이 해당 단위를 명시해주지 않아도 된다. 만약 명시해준다면  기본타입은 무시가 되고 작성한 타입으로 동작된다.

AnnotatedClassFinder

이 클래스는 Spring boot 2.1 에 새롭게 나타난 클래스이다. 그렇다고 해서 완전하게 새로운 클래스는 아니다.(?) 기존의 있던 SpringBootConfigurationFinder 클래스를 살짝 변경한? 클래스이다. 기존에는 @SpringBootConfiguration 어노테이션만을 파싱하기 위한 클래스라면 AnnotatedClassFinder  클래스 경우에는 원하는 어노테이션을 파싱할 수 있다. 

SpringBootConfigurationFinder() {
    this.scanner = new ClassPathScanningCandidateComponentProvider(false);
    this.scanner.addIncludeFilter(
            new AnnotationTypeFilter(SpringBootConfiguration.class));
    this.scanner.setResourcePattern("*.class");
}

위의 코드는 기존의 클래스이며 아래의 코드는 새롭게 탄생한 코드이다.

public AnnotatedClassFinder(Class<? extends Annotation> annotationType) {
    Assert.notNull(annotationType, "AnnotationType must not be null");
    this.annotationType = annotationType;
    this.scanner = new ClassPathScanningCandidateComponentProvider(false);
    this.scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    this.scanner.setResourcePattern("*.class");
}

차이라곤 뭐 생성자에 어노테이션을 받는 부분이 추가 된 것 뿐이다.  

사용법은 해당 클래스를 보면 쉽게 알 것 같다.  그래서 생략 하겠다..

PropertyMapper

PropertyMapper 클래스는 Spring boot 2.0 에서 추가된 클래스이다.
이 클래스의 용도는 프로퍼티들을 매핑 시켜주는? 그런 역할을 하는 클래스이다. 예를들어 Spring boot 의 @ConfigurationProperties 를 사용해서 프로퍼티들을 매핑 시켰다면 그 이후에 그 프로퍼티들을 실제 사용하는 프로퍼티에 매핑을 시켜주는 그런? 클래스이다. 흠 말로는 어려우니 코드를 살펴보도록 하자.

class FooProperties {
    private int timeout;
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }
    public int getTimeout() {
        return timeout;
    }
}

class FooTemplate {
    private int timeout;
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }
    public void bar() {
        //...
        //this.timeout
    }
}

예를 들어 위와 같은 클래스가 있다고 가정하자. FooProperties 클래스는 클라이언트로부터 프로퍼티를 받는 클래스이고 FooTemplate 은 실제 그 프로퍼티로 어떤 작업등을 하는 클래스라고 가정해보자. 그럼 다음과 같이 사용할 수 있다.

PropertyMapper map = PropertyMapper.get();
FooProperties properties = new FooProperties();
properties.setTimeout(10);
FooTemplate fooTemplate = new FooTemplate();
map.from(properties::getTimeout).to(fooTemplate::setTimeout);

위와 같이 FooProperties 클래스로 부터 받은 프로퍼티를 FooTemplate 의 속성으로 넣는 작업이다. 실제로 FooTemplate 의 timeout 속성에는 10이라는 값이 들어가 있다. 

또한 좀 더 나은 방식도 제공해준다. 예를들어 source 의 프로퍼티가 null일 경우에 굳이 target 프로퍼티에게 null을 넣을 필요가 없다면 다음과 같이 작성하면 된다.

map.from(properties::getTimeout).whenNonNull().to(fooTemplate::setTimeout);

그러면 fooTemplate 클래스에 timeout은 영향 받지 않는다. 또한 간단하게 타입도 변경할 수 있다. 예를들어  FooTemplate의 timeout의 속성이 String이라면 다음과 같이 타입도 변경할 수 있다.

map.from(properties::getTimeout).as(String::valueOf).to(fooTemplate::setTimeout);

필자가 말한 위의 메서드뿐만아니라 많은 메서드가 존재하니 필요하다면 한번씩 살펴보는 것도 나쁘지 않다.

TestPropertyValues

이 클래스는 Spring boot 2.0에 새로생긴 클래스 이다. 하지만 우리가 딱히 직접적으로 사용할 클래스는 아니다.  기존의 존재했던 클래스 (EnvironmentTestUtils) 클래스가 Deprecated 되고 해당 클래스가 생겼다.

예전에 Test 할 때 주로 EnvironmentTestUtils.addEnvironment 메서드를 사용했지만 이제는 TestPropertyValues를 사용하면 되는데 해당 클래스를 직접적으로 사용할 일은 없다. 뭐 있을 수는 있겠지만 딱히 커스텀하게 만들지 않는 이상은..

왜냐하면 기존의 Config를 테스트 하던 클래스가 Spring boot 2.0 부터 새롭게 바뀌면서 ApplicationContextRunner 를 이용하면 되기 때문이다. 

예전에 RC 버전일때 포스팅한 내용은 여기있다. 궁금하다면 참고하면 되겠다.

여기에 존재하는 withPropertyValues 가 내부적으로 바로 TestPropertyValues 이다. 하지만 우리는 그냥 String 타입으로 넘겨서 알아채지 못했을 수도 있다.

private final TestPropertyValues environmentProperties;

//...

public SELF withPropertyValues(String... pairs) {
    return newInstance(this.contextFactory, this.initializers,
            this.environmentProperties.and(pairs), this.systemProperties,
            this.classLoader, this.parent, this.configurations);
}

딱히 사용할일은 없지만 그래도 이런게 있다고는 알아봤다. 오늘 내용은 Spring 의 새로운 클래스들을 몇개 알아봤다. 물론 다 알아 보고 싶지만 그러지 못한다. 깊은 내용의 클래스들도 있고 뭐가 뭔지 모르는 클래스도 있고 아직 확인 못한 클래스도 있으니.. 오늘은 여기까지만 알아보도록 하자. 추후에 더 알아 볼 수 있으면 알아보도록 하자. 

유틸성 클래스들이라 해당 클래스 직접사용해봐도 쉽게 이해할 수 있을 것으로 판단 된다. 

그럼 오늘은 이만!