
Spring BootのLazy InitializationをKotlinで試してみた
はじめに
Spring Boot 2.2.0からLazy Initializationが簡単に導入できるようになりました。Lazy Initializationを設定するとBeanを必要になるまで生成を遅らせます。メリットとしてはアプリケーションの起動速度の向上などの効果があります。サンプルを使って紹介したいと思います。
環境
- Spring Boot 2.2.5
- Kotlin 1.3.61
参考リンク
- https://spring.io/blog/2019/10/16/spring-boot-2-2-0
- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#lazy-initialization
- https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2
設定
次の設定を追加するだけで、BeanのLazy Initializationが有効になります。
spring:
main:
lazy-initialization: trueLazy Initialization対象外にする方法
Lazy Initializationの対象外する方法として、次の方法があります。
- @Lazy(false)のアノテーションを付加する
- LazyInitializationExcludeFilterに対象外とするBeanの型を登録する
私は自分で定義するBeanに関しては@Lazy(false)を付加し、ライブラリが提供するBeanに関してはLazyInitializationExcludeFilterで対象外としています。
コード
今回のサンプルでメインに使用するクラスです。インスタンスを生成したタイミングで標準出力、printメソッドで標準出力します。
class DemoBean(private val beanName: String) {
init {
println("constructor:$beanName")
}
fun print() {
println("method:$beanName")
}
}
LazyInitializationExcludeFilterでLazy Initializationの対象外とするためのクラスです。先程のDemoBeanと実装内容は同じです。
class ExcludeDemoBean(private val beanName: String) {
init {
println("constructor:$beanName")
}
fun print() {
println("method:$beanName")
}
}Beanを生成するクラスです。このクラスでは次のBeanを生成しています。
- Lazy Initializationが有効となる「lazyDemoBean」
- @Lazy(false)を付加した「notLazyDemoBean」
- LazyInitializationExcludeFilterで指定された「excludeDemoBean」
- Lazy Initialization対象外を指定する「lazyInitializationExcludeFilter」
※こちらのLazyIntializationExludeFilterのサンプルでは、Javaのコードになっており、staticメソッドでLazyInitializationExcludeFilterのBeanを生成していました。Kotlinでは、companion objectのメソッドとすることで動作しました。
import org.springframework.boot.LazyInitializationExcludeFilter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Lazy
@Configuration
class DemoBeanFactory {
@Bean("lazyDemoBean")
fun lazyDemoBean(): DemoBean {
return DemoBean("lazyDemoBean")
}
@Bean("notLazyDemoBean")
@Lazy(false)
fun notLazyDemoBean(): DemoBean {
return DemoBean("notLazyDemoBean")
}
@Bean
fun excludeDemoBean(): ExcludeDemoBean {
return ExcludeDemoBean("excludeDemoBean")
}
companion object {
@Bean
fun lazyInitializationExcludeFilter(): LazyInitializationExcludeFilter {
return LazyInitializationExcludeFilter.forBeanTypes(ExcludeDemoBean::class.java)
}
}
}メインクラスの実装です。アプリケーションを開始したあとに、ApplicationContextから各Beanを取り出し、printメソッドを呼び出しています。
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class LazyInitializationDemoApplication
fun main(args: Array<String>) {
println("ApplicationContextの初期化 開始")
val ctx = runApplication<LazyInitializationDemoApplication>(*args)
println("ApplicationContextの初期化 完了")
val notLazyDemoBean = ctx.getBean("notLazyDemoBean",DemoBean::class.java)
notLazyDemoBean.print()
val excludeDemoBean = ctx.getBean("excludeDemoBean",ExcludeDemoBean::class.java)
excludeDemoBean.print()
val lazyDemoBean = ctx.getBean("lazyDemoBean",DemoBean::class.java)
lazyDemoBean.print()
}標準出力の結果は次のようになります。Lazy Initialization対象外にしたBeanはApplicationContextの初期化中にBeanが生成されているのがわかります。Lazy Initializationの対象となるlazyDemoBeanについてはApplicationContextから取得したタイミングでBeanが生成されているのがわかると思います。
ApplicationContextの初期化 開始 constructor:notLazyDemoBean constructor:excludeDemoBean ApplicationContextの初期化 完了 method:notLazyDemoBean method:excludeDemoBean constructor:lazyDemoBean method:lazyDemoBean
終わりに
簡単にLazy Initializationの設定ができることがわかったと思います。開発環境やテスト実行時などに起動速度を上げたい場合に有効です。
参考リンクに言及がありますが、Lazy Initializationを有効にしていない場合、起動時にクラス定義が見つからないエラー、メモリ不足エラー、構成の誤りによるエラーなどが発生しますが、Lazy Initializationを有効にすることで発生を遅らせることがあります。本番環境では使用せずに、本番同等の環境でLazy Initializationを無効化したテストを実施することを考慮するべきだと思います。