スプリングコア原理08]アイドルライフサイクルコールバック


この文章はInforning金英漢先生課をまとめました!
今回の記事では、空のライフサイクルの生成と終了について説明します.
アプリケーションの開始時に複数のアプリケーション(データベース接続プールやネットワークソケットなど)に予め接続し、終了時にすべて閉じる場合は、初期化と閉じる操作が必要です.
これらの初期化および終了操作の方法をスプリングシートで説明します.
リファレンス
ここで、初期化はオブジェクトを作成するだけでなく、すべての準備を完了して、自分のタスクを実行できるようにします.

1.空のライフサイクルコールバックの開始


外部ネットワークに予め接続されたオブジェクトを作成するとします.
ネットワーククライアントは、アプリケーションの起動時にconnect()を呼び出して接続を確立し、アプリケーションの終了時にdisconnect()を呼び出して接続を切断します.
package hello.core.lifecycle;

public class NetworkClient {
    
    private String url;
    
    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
        connect();
        call("초기화 연결 메시지");
    }
    
    public void setUrl(String url) {
        this.url = url;
    }
    
    //서비스 시작시 호출
    public void connect() {
        System.out.println("connect: " + url);
    }
    
    public void call(String message) {
        System.out.println("call: " + url + " message = " + message);
    }
    
    //서비스 종료시 호출
    public void disconnect() {
        System.out.println("close: " + url);
    }
}
これで、スプリングコンテナにオブジェクトを登録し、初期化操作を続行します.
package hello.core.lifecycle;

import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class BeanLifeCycleTest {
    
    @Test
    public void lifeCycleTest() {
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
        NetworkClient client = ac.getBean(NetworkClient.class);
        ac.close(); // 스프링 컨테이너 종료.
    }
    
    @Configuration
    static class LifeCycleConfig {
        
        @Bean
        public NetworkClient networkClient() {
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://hello-spring.dev");
            return networkClient;
        }
    }
}
実際には、スプリングコンテナはclose()メソッドで終了することもできますが、実際には終了することは多くありません.そのため、アプリケーションコンテキストの構成(デフォルトのインタフェースApplicationContextよりはるかに低い)から入手できます.
要するに、Network Clientオブジェクトを作成し、setUrlによって初期化操作に必要なurlを注入しました.
ただし、実行時に次の結果が呼び出されます.
생성자 호출, url = null
connect: null
call: null message = 초기화 연결 메시지
オブジェクトを作成するにはurlがまだ存在しないため、オブジェクトを作成した後に外部注入修飾子でseturl()を呼び出す場合にのみurlが存在するため、通常の初期化操作はできません.
同様に、スプリングシートには、オブジェクトの作成->注入依存関係のライフサイクルがあります.(作成者注入を除く)
したがって、ネットワーククライアントの初期化操作がURL注入完了後に呼び出さなければならないように、スプリングビンの初期化操作は、オブジェクト作成と依存関係注入完了後に行わなければならない.
では、開発者はどのように依存関係注入が終わる時間を知っているのでしょうか.
依存関係注入が完了すると、スプリングは様々な機能を提供し、スプリングビンがいつ初期化されるかをコールバック法で通知することができる.また、スプリングコンテナが終了する前に、消灯コールバックが発行されます.
このため、スプリングシートのイベントライフサイクルを以下のように整理することができる.
スプリングコンテナの作成->スプリング空席の作成->依存関係の注入->コールバックの初期化->使用->前コールバックのキャンセル->スプリングの終了
リファレンス
Network Clientの例では、seturlではなくジェネレータからurlを直接追加することを考慮できます.ただし、作成者はパラメータを受信し、プライマリラインを指定してオブジェクトを作成します.逆に、初期化では、外部コネクタへの接続など、生成された値を使用して複雑なタスクが実行されます.
したがって、メンテナンスの観点から、これらのタスクを作成者で同時に実行するよりも、オブジェクトの作成部分と初期化部分を明確に区別することが重要です.
リファレンス
スプリング容器が閉じるとともに消えてしまうため、閉じる前にコールバックが発生します.
逆に、場合によっては、空はコンテナに関係なく空が消える前にコールバックされます.

2.インタフェース初期化bean、DisposableBean


InitializationBeanインタフェースとDisposableBeanインタフェースを使用して、依存関係注入が終了したことを通知します.
package hello.core.lifecycle;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class NetworkClient implements InitializingBean, DisposableBean {
    
    private String url;
    
    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
    }
    
    public void setUrl(String url) {
        this.url = url;
    }
    
    public void connect() {
        System.out.println("connect: " + url);
    }
    
    public void call(String message) {
        System.out.println("call: " + url + " message = " + message);
    }
    
    public void disConnect() {
        System.out.println("close + " + url);
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        connect();
        call("초기화 연결 메세지");
    }
    
    @Override
    public void destroy() throws Exception {
        disConnect();
    }
}
InitializingBean、DisposableBeanインタフェースがそれぞれ受信された場合、afterProperties Set()およびdestroy()メソッドが提供されます.
afterProperties Set()は、依存関係注入の終了時に呼び出されるコールバックメソッドで、ここで必要な初期化操作を実行できます.
destroy()は,消える前に呼び出されるコールバックメソッドであり,名前から推定できる.
생성자 호출, url = null
NetworkClient.afterPropertiesSet
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메세지  
13:24:49.043 [main] DEBUG
org.springframework.context.annotation.AnnotationConfigApplicationContext -
Closing NetworkClient.destroy
close + http://hello-spring.dev
出力結果は,通常url注入後にメソッドが呼び出され,springコンテナの終了を呼び出すと破棄メソッドが呼び出されることを示した.
これらのインタフェースを使用する方法は2003年に登場したが,現在は主により良い方法を使用している.
理由は次のとおりです.
  • はスプリング専用インタフェースであり、コードはスプリング専用インタフェースに依存する.
  • 初期および消滅方法名は変更できません.
  • は、コードを変更できない外部ライブラリに適用できません.
    外部ライブラリを空に登録しようとしても修復できない場合は、インタフェースを引き込むことでメソッドを作成できないため、
  • 3.空の登録を初期化し、消滅方法を指定する


    注入が完了したことを示す2番目の依存関係を解消する方法を指定できます.
    package hello.core.lifecycle;
    
    public class NetworkClient {
    
        private String url;
    
        public NetworkClient() {
            System.out.println("생성자 호출, url = " + url);
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public void connect() {
            System.out.println("connect: " + url);
        }
    
        public void call(String message) {
            System.out.println("call: " + url + " message = " + message);
        }
    
        public void disConnect() {
            System.out.println("close + " + url);
        }
    
        public void init() {
            System.out.println("NetworkClient.init");
            connect();
            call("초기화 연결 메서드");
        }
    
        public void close() {
            System.out.println("NetworkClient.close");
            disConnect();
        }
    }
    まず、NetWork Clientは、さっき引き出したインタフェースとメソッドを削除し、init()、close()という初期化と破棄方法を作成しました.△名前を変えてもいいです.
    @Bean(initMethod = "init", destroyMethod = "close")
    public NetworkClient networkClient() {
        NetworkClient networkClient = new NetworkClient();
        networkClient.setUrl("http://hello-spring.dev");
        return networkClient;
    }
    次に,@BeanはinitMethodとdestroyMethodを先ほど作成したメソッドとして指定する.
    생성자 호출, url = null
    NetworkClient.init
    connect: http://hello-spring.dev
    call: http://hello-spring.dev message = 초기화 연결 메서드
    13:26:44.433 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a5a4e19, started on Tue Jan 18 13:26:44 KST 2022
    NetworkClient.close
    close + http://hello-spring.dev
    正常に初期化されていることがわかります.
    上記の方法では、スプリングシートやコードに依存せずに、方法名を自由に選択できます.
    また、設定情報が使用されているため、コードを修復できない外部ライブラリに初期化と終了方法を適用することもできます(名前を指定するだけでいい).
    注意:終了方法の推論
    @BeanのdestroyMethodプロパティが指定されていない場合、closeとshutdownという終了メソッドが自動的に検索され、呼び出されます.
    destroyMethodのデフォルト値は推定されるからです.
    したがって、メソッド名をcloseとshutDownと命名すると、設定情報を登録する必要がなく、自動的に「非表示」メソッドが呼び出されます.
    この機能を使用したくない場合は、スペースをdestroyMethod=""に指定するだけです.

    4.プレゼンテーション@PostConstruct,@Predestroy


    とにかくこれをそのまま使えばいい
    package hello.core.lifecycle;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    public class NetworkClient {
        
        private String url;
        
        public NetworkClient() {
            System.out.println("생성자 호출, url = " + url);
        }
        
        public void setUrl(String url) {
            this.url = url;
        }
        
        public void connect() {
            System.out.println("connect: " + url);
        }
        
        public void call(String message) {
            System.out.println("call: " + url + " message = " + message);
        }
        
        public void disConnect() {
            System.out.println("close + " + url);
        }
        
        @PostConstruct
        public void init() {
            System.out.println("NetworkClient.init");
            connect();
            call("초기화 연결 메서드");
        }
        
        @PreDestroy
        public void close() {
            System.out.println("NetworkClient.close");
            disConnect();
        }
    }
    初期化方法では、@PostConstruct、破棄方法で@PreDestoyプレゼンテーションを追加します.
    @Bean
    public NetworkClient networkClient() {
        NetworkClient networkClient = new NetworkClient();
        networkClient.setUrl("http://hello-spring.dev");
        return networkClient;
    }
    先ほど追加した@Beanのその他の設定を削除します.
    생성자 호출, url = null
    NetworkClient.init
    connect: http://hello-spring.dev
    call: http://hello-spring.dev message = 초기화 연결 메서드
    13:26:44.433 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a5a4e19, started on Tue Jan 18 13:26:44 KST 2022
    NetworkClient.close
    close + http://hello-spring.dev
    出力を確認すると、通常の初期化と破棄方法が呼び出されていることがわかります.
    この方法は非常に簡単で、最新のスプリングが推奨する方法です.
    またjavaxは、Java標準JSR-250であり、スプリングに依存する技術ではないため、スプリングではなく他の容器で動作することも可能である.
    ただし、外部ライブラリには適用されません.(いずれにしても、コードを変更する必要があります)
    したがって、デフォルトではこの方法を使用することをお勧めします.ない場合は@Beanの機能を使用します.