몇일전에 Spring 5.2.M1버전 과 Spring boot2.2.M2 버전이 릴리즈 되었다. 아직 해당 프로젝트들의 GA 버전이 나오기엔 시간이 많이 남아있지만 (대략 7월) 많이 변경되지 않을 것들만 모아서 살펴보도록 하자. (사실 필자가 아는 부분만이겠지만)

일단 Spring Framework 5.2 부터 알아보자.

Spring Framework 5.2

@Configuration

@Configuration 어노테이션의 속성이 추가 되었다. proxyBeanMethods 라는 속성이며 예전에 필자가 포스팅한 글중 Lite Mode라는 주제가 있었던 그 내용이 Spring 쪽에 들어갔다.
사용법은 아주 간단하다.

@Configuration(proxyBeanMethods = false)
public class TestConfiguration  {
  //... 
}

위와 같이 lite mode를 적용하고 싶으면 proxyBeanMethods 속성을 false로 설정하면 된다. 기본값은 true이다. 그럼 cglib을 사용하지 않는 상태에서 설정이 진행된다.
해당 설명은 위의 포스팅이 있기에 생략.

참고로 Spring boot 2.2의 AutoConfiguration들은 모두 @Configuration(proxyBeanMethods = false) 로 변경하였다.
또한 @SpringBootApplication 어노테이션에도 proxyBeanMethods 속성이 추가 되었다.

vavr의 Try와 @Transactional

io.vavr(구 javaslang) 의 Try를 @Transactional 어노테이션을 지원한다.

@Transactional
public Try<?> hello() {
    return Try.of(() -> accountRepository.save(new Account(1L, "test")))
        .filter(Account::isActive)
        .andThenTry((account) -> accountRepository.save(new Account(account.getId()))); // 예외
}

해당 라인에서 예외가 발생하면 롤백이 진행된다. 필자가 vavr를 잘 사용하지 못하는 관계로..
만약 vavr 프로젝트를 잘 사용한다면 유용하게 쓰일듯 싶다. Spring data 쪽에서는 vavr 프로젝트도 지원해주니 함께 사용하면 더 많은 지원을 받을 수 있을 듯 싶다.

import io.vavr.control.Try;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AccountRepository extends JpaRepository<Account, Long> {

    Try<Account> findByName(String name);

}

MergedAnnotations

Annotation을 처리하기 위해 새로운 API가 등장했다. 기존의 있던 AnnotationUtilsAnnotatedElementUtils 대신에 사용할 수 있는 API이다.
위의 AnnotationUtils, AnnotatedElementUtils API들도 내부적으로 MergedAnnotations 으로 변경하였다.
조금 더 살펴보고 해야겠지만 일단은 기본사용법만 알아두자. 언제 바뀔지 모르니..

MergedAnnotation<SpringBootApplication> springBootApplication = MergedAnnotations.from(Application.class)
    .get(SpringBootApplication.class);
Class<?>[] scanBasePackageClasses = springBootApplication.getClassArray("scanBasePackageClasses");
String[] scanBasePackages = springBootApplication.getStringArray("scanBasePackages");
boolean proxyBeanMethods = springBootApplication.getBoolean("proxyBeanMethods");

위와 같이 쉽게 사용할 수 있다. 더 많은 API 들과 속성들이 있긴 하지만 지금 당장은 살펴볼 필요가 없어서 이정도만 알아봤다. 릴리즈 되면 그때 다시 알아보도록 하자.
근데 사용할 일이 많이 없어서..

Spring boot 2.2

지금 부터는 Spring boot 쪽의 변화이다.

lazyInitialization

Spring boot 2.2 부터는 lazyInitialization를 모든 빈의 적용할 수 있게 되었다. 기존에는 @Lazy 어노테이션을 선언하여 게으른 빈은 설정하였다.

@Bean
@Lazy
public AccountService accountService() {
    return new AccountService();
}

위와 같이 설정하면 실제 사용될 시점에 빈이 생성된다.
조금 빠른 로딩을 하기 위해서 모든 빈에 게으르게 빈을 설정 할 수 있게 되었다.

spring.main.lazy-initialization=true

application.properties에 위와 같이 spring.main.lazy-initialization 속성을 true로 주면 모든 빈들이 Lazy 빈으로 생성 된다.
Spring boot 로딩 시간이 길다면 해당 속성을 이용해서 빠르게 로딩할 수 있다.

하지만 여기서 주의할점은 web 기준으로 http 첫 요청이 조금 길다는점. 첫 요청시 사용하는 빈들이 그 순간 만들어져 요청이 조금 느릴 수 있다.
두 번쨰로는 일반적으로는 빈을 찾을 수 없을 때 에러가 나지만 lazyInitialization을 설정하면 서버가 기동될때 에러를 찾을 수 없다. 빈을 사용할 때 그 때 빈을 찾으므로 런타임시 에러가 발생할 수 있으므로 조심해야 된다.

개발할 때만 설정하고 운영할 때는 설정 하지 않는것이 좋아 보인다.

만약 위의 설정을 해놓고 특정빈은 Lazy 로딩을 하고 싶지 않다면 다음과 같이 설정하면 된다.

@Bean
@Lazy(false)
public AccountService accountService() {
    return new AccountService();
}

이 기술(@Lazy)은 오래된 기술이다. 실제로는 대략 11년전에 개발된 기술이다. 그래서 우리도 간단한 구현만으로도 위와 비슷한 모든 빈에 Lazy 초기화를 할 수 있다.

@Bean
static BeanFactoryPostProcessor beanPostProcessor() {
    return beanFactory -> {
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for(String beanName : beanDefinitionNames) {
            beanFactory.getBeanDefinition(beanName).setLazyInit(true);
        }
    };
}

Spring boot 2.2를 사용하지 않더라도 그 이하버전에서도 위와 같이 구현하면 게으른 초기화를 진행 할 수 있다.

@ConfigurationPropertiesScan

@ConfigurationPropertiesScan 어노테이션이 추가 되었다. 애노테이션명 그대로 ConfigurationProperties를 스캔하는 용도이다.

@ConfigurationProperties("foo")
public class FooProperties {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

위와 같은 Properties가 존재한다면 우리는 아래와 같이 설정하였다.

@Configuration
@EnableConfigurationProperties(FooProperties.class)
public class Application {

}

이제는 EnableConfigurationProperties을 사용하지 않고 @ConfigurationPropertiesScan 어노테이션을 사용하여 scan 할 수 있다.

@Configuration
@ConfigurationPropertiesScan("ml.wonwoo")
public class Application {

}

위와 같이 적용하면 ml.wonwoo 패키지 아래의 모든 프로퍼티들이 자동으로 등록이 된다.
Spring boot 2.2에서는 @SpringBootApplication 애노테이션에 메타 애노테이션으로 @ConfigurationPropertiesScan 애노테이션이 추가 되었으므로 추가적인 설정은 할필요 없다.

scan할 패지키를 지정하지 않는다면 선언된 애노테이션 이하의 패지키를 검색한다.

이제 @EnableConfigurationProperties의 사용처는 자동설정을 커스텀하게 만들때 사용하면 된다.

Immutable @ConfigurationProperties

불변의 Properties 를 지원한다. 기존에는 기본생성자와 setter가 존재 했어야 했지만 이제는 그럴 필요 없다.

@ConfigurationProperties("foo")
public class FooProperties {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

기존에는 위와 같이 javaBeans 스펙에 따라야 했지만 이제는 setter 또한 필요 없고 기본생성자 또한 필요 없다.

@ConfigurationProperties("foo")
public class FooProperties {

    private final String name;

    public FooProperties(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

만약 해당 프로퍼티에 디폴트값을 원한다면 @DefaultValue 어노테이션을 이용해서 기본값을 설정 할 수 있다.

@ConfigurationProperties("foo")
public class FooProperties {

    private final String name;

    public FooProperties(@DefaultValue("wonwoo") String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

이렇게 하면 프로퍼티의 값이 존재 하지 않아도 기본값으로 wonwoo가 설정이 된다.

오늘은 이렇게 Spring과 Spring boot에 변화에 대해서 알아봤다. 이 보다 많은 변화가 있었지만 개발자들이 직접적으로 사용하기에는 위의 내용으로도 충분해 보여 몇가지만 준비했다.
더 많은 내용은 해당 문서를 통해 알아보도록 하고, 위의 내용 또한 언제 바뀔지 모르니 현재는 그냥 참고만 하면 되겠다.
나중에 릴리즈 될때 다시 보자!