9
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

Spring Boot 2.2.0でLazy Initializationを試してみた

Spring Boot v2.2.0

Spring Boot v2.2.0がリリースされ、GAとなりました。

なので、その機能の一つであるLazyInitializationについて検証してみます。

Spring Boot 2.2.0
Release v2.2.0.RELEASE · spring-projects/spring-boot · GitHub
Spring Boot 2.2 Release Notes · spring-projects/spring-boot Wiki · GitHub
Spring Boot Reference Documentation

環境

  • Windows10
  • OracleJDK 13(build 13)
  • Spring Boot 2.2.0 RELEAS

Projectの作成

ひながたとなるProjectは、Spring Initializrから作成します。

spring-initializr.PNG

Gradle

Spring Initializrで作成したGradleプロジェクトでは、Gradle 5.6.2が適用されることを確認しました。

> gradlew -version

------------------------------------------------------------
Gradle 5.6.2
------------------------------------------------------------

Build time:   2019-09-05 16:13:54 UTC
Revision:     55a5e53d855db8fc7b0e494412fc624051a8e781

Kotlin:       1.3.41
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.14 compiled on March 12 2019
JVM:          13-ea (Oracle Corporation 13-ea+33)
OS:           Windows 10 10.0 amd64

Lazy Initialization

ApplicationContextで管理するBeanの生成を、アプリケーションの起動時(≒ApplicationContextの初期化時)ではなくて、対象のBeanの呼び出しがされる際に行うのが、Lazy Initializationです。

この機構は以前のバージョンからもありますが、v2.2.0からはpropertyspring.main.lazy-initializationにtrueを適用することで、一律LazyInitializationが適用されます。

  • application.yml
spring:
  main:
    lazy-initialization: true
  • Bean class
package jp.co.musako.domain.model;

public class LazyInitializationDemoBean {

    private String message;

    public LazyInitializationDemoBean(String message) {
        this.message = message;
        System.out.println("call constructor " + this.message);
    }

    public void writer(String message){
        System.out.println(this.message + " is " + message);
    }
}
  • config class
package jp.co.musako.config;

import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    @Bean("lazyInitializationDemoBean1")
    public LazyInitializationDemoBean lazyInitializationDemoBean1(){
        return new LazyInitializationDemoBean("create lazyInitializationDemoBean1");
    }

    @Bean("lazyInitializationDemoBean2")
    public LazyInitializationDemoBean lazyInitializationDemoBean2(){
        return new LazyInitializationDemoBean("create lazyInitializationDemoBean2");
    }
}
  • main class
package jp.co.musako;

import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
        System.out.println("Initialize Application Context");

        LazyInitializationDemoBean bean1 = ctx.getBean("lazyInitializationDemoBean1", LazyInitializationDemoBean.class);
        bean1.writer("first bean");

        LazyInitializationDemoBean bean2 = ctx.getBean("lazyInitializationDemoBean2", LazyInitializationDemoBean.class);
        bean2.writer("second bean");
    }
}

実行結果

>gradlew bootRun

> Task :bootRun

2019-10-23 18:16:37.620  INFO 12708 --- [           main] jp.co.musako.Application                 : Started Application in 4.295 seconds (JVM running for 4.933)
Initialize Application Context
call constructor lazyInitializationDemoBean1
lazyInitializationDemoBean1 is first bean
call constructor lazyInitializationDemoBean2
lazyInitializationDemoBean2 is second bean

ここで、以下の順序で処理がされていることがわかります。

  1. アプリケーション起動
    • 起動後に「Initialize Application Context」を出力
  2. Beanの生成
    • Beanのコンストラクタ内で「call constructor lazyInitializationDemoBean1」を出力
  3. 生成したBeanのwriterメソッドが呼び出される
    • writerメソッドで「lazyInitializationDemoBean1 is first bean」を出力

Lazy Initializationの適用除外

spring.main.lazy-initialization=trueを設定してLazyInitializationをプロジェクトに適用しつつ、特定のBeanをLazyInitializationの対象外とするには以下の方法があります。

  1. @org.springframework.context.annotation.Lazy(false)をBeanに設定する
  2. org.springframework.boot.LazyInitializationExcludeFilterに対象外とするBeanを登録する
  • config class
package jp.co.musako.config;

import jp.co.musako.domain.model.ExcludeLazyInitializationDemoBean;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class Config {

    @Bean
    static LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() {
        return LazyInitializationExcludeFilter.forBeanTypes(ExcludeLazyInitializationDemoBean.class);
    }

    // Lazy Initialization適用対象
    @Bean("lazyInitializationDemoBean1")
    public LazyInitializationDemoBean lazyInitializationDemoBean1(){
        return new LazyInitializationDemoBean("lazyInitializationDemoBean1");
    }

    // Lazy Initialization適用対象外
    @Bean("lazyInitializationDemoBean2")
    @Lazy(false)
    public LazyInitializationDemoBean lazyInitializationDemoBean2(){
        return new LazyInitializationDemoBean("lazyInitializationDemoBean2");
    }

    // Lazy Initialization適用対象外
    @Bean
    public ExcludeLazyInitializationDemoBean ExcludeLazyInitializationDemoBean(){
        return new ExcludeLazyInitializationDemoBean("excludeLazyInitializationDemoBean");
    }
}
  • main class
package jp.co.musako;

import jp.co.musako.domain.model.ExcludeLazyInitializationDemoBean;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
        System.out.println("Initialize Application Context");

        LazyInitializationDemoBean bean1 = ctx.getBean("lazyInitializationDemoBean1", LazyInitializationDemoBean.class);
        bean1.writer("first bean");

        LazyInitializationDemoBean bean2 = ctx.getBean("lazyInitializationDemoBean2", LazyInitializationDemoBean.class);
        bean2.writer("second bean");

        ExcludeLazyInitializationDemoBean ExcludeLazyInitializationBean = ctx.getBean("ExcludeLazyInitializationDemoBean", ExcludeLazyInitializationDemoBean.class);
        ExcludeLazyInitializationBean.writer("third bean");
    }

}

  • 実行結果
>gradlew bootRun

> Task :bootRun

call constructor lazyInitializationDemoBean2
call constructor excludeLazyInitializationDemoBean
2019-10-23 18:52:52.464  INFO 6004 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-10-23 18:52:52.468  INFO 6004 --- [           main] jp.co.musako.Application                 : Started Application in 2.978 seconds (JVM running for 3.454)
Initialize Application Context
call constructor lazyInitializationDemoBean1
lazyInitializationDemoBean1 is first bean
lazyInitializationDemoBean2 is second bean
excludeLazyInitializationDemoBean is third bean

上記のように、Config.javaで定義した3つのBeanのうち、lazyInitializationDemoBean1のみがLazyInitializationの対象となることがわかります。

ソースコードはこちら:GitHub - forests-k/spring-boot-lazy-initialization
: Spring Boot v2.2.0 lazy-initialization sample

Lazy Initializationの適用時の検討事項

Lazy Intializationを導入するメリットは、アプリケーション起動時の速度が向上すること、必要な時に初期化するため無駄にメモリを消費しないことにあります。

しかし、必要な時にBeanを初期化してコンテナにBeanを保持するということは、これまでのBeanのライフサイクルが一部変更となり、コンテナ内で保持するBeanの最大となりうる数量(=メモリ占有量)がみえないことがあるので、CPUへの負荷やロードアベレージへの影響がある、すなわちLazyInitializationを過信しすぎてはいけない、という可能性があるかもしれないです。

そのため、テスト環境や本番同等の環境で一時的にLazy Initializationを無効化するなどのテストを実施することも視野に入れるべきと思われます。

ref

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
9
Help us understand the problem. What are the problem?