(Spring) enum이나 object를 초기화 하기
[!info] Spring과 무관하게
- 컴파일 타임 초기화 -> 불가능
- 앱 시작 시점에 초기화 -> 가능 한 경우 사용 가능한 방법이다.
앱 시작 시점에 private 변수를 nested class bean을 이용해 초기화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class InitAtStartup {
private static Object bean1;
private static Object property1;
public static Object getBean1() {
return bean1;
}
public static Object getProperty1() {
return property1;
}
@Component
private static class Init {
public Init(Object bean1, @Value("${prop}") String property1) {
InitAtStartup.bean1 = bean1;
InitAtStartup.property1 = property1;
}
}
}
kotlin object
나, enum
도 위와 같은 방식으로 초기화 가능하다.
사례
.properties를 담고 있는 Spring 영역 바깥의 Object
.properties
나 yml
에 있는 환경에 따라 달라지는 속성(프로퍼티)들을 가져올 때 (e.g., url)
DI로 가져오기 때문에, Bean이 아닌 DTO 같은 클래스에서는 url 속성 등에 접근 할 수 없다.
즉 이런 데이터는 반드시 Bean으로부터 넘겨 받아야 하는데, 이 점이 생각을 표현하는데 허들이 되는 경우가 종종 있다.
=> DTO 같은 Spring 영역 바깥에 존재하는 클래스에서도 자유롭게 참조 할 수 있는 Singleton 객체가 필요하다.
Spring 영역 바깥에서 singleton 만드려면, 컴파일 타임에 생성되는 object
키워드를 이용하게 되는데,
이런 프로퍼티들은 애플리케이션 시작 시점에 적절한 프로파일 별 .yml
을 읽어와야 알 수 있으므로 컴파일 타임에 세팅되는 정적 데이터는 아니다.
따라서 Object.field
는 var
처리 할 수 밖에 없다.
var
이므로 런타임에 언제든 변경 될 수 있다는 위험을 안고 있어, 이를 방지할 안전장치가 필요하다.
=> private
처리하고 nested class가 초기화
Spring 바깥 영역에서 사용해야 하는 객체
예를 들면 SpringBoot에서 제공하는 Auto-config가 적용된 objectMapper
를, DTO에서 바로 접근해서 사용하고 싶은 경우가 있다. 이런 경우 static OBJECT_MAPPER
변수 처리하고 nested bean에서 초기화하면, 애플리케이션 시작 시점 이후에는 사용 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// object에 담는 방식
object CoroutineScope {
val IO: CoroutineScope by lazy {
SpringContext.applicationContext.getBean(CoroutineScope::class.java)
}
}
// companion에 담는 방식
@Component
class CoroutineScope(ioCoroutineScope: CoroutineScope) {
init {
Companion.IO = ioCoroutineScope
}
companion object {
lateinit var IO: CoroutineScope
private set
}
}