【Spring】 (4) Beanのスコープとライフサイクル


序文

Bean は Spring フレームワークの中核概念であり、Spring コンテナーによって管理されるオブジェクト インスタンスを指しますSpring を使用して開発する場合、通常、アプリケーションのさまざまな機能やコンポーネントを実行するさまざまな Bean を定義しますしかし、多くの開発者は、Bean の定義と使用法にのみ注意を払い、Bean のスコープとライフサイクルを無視する可能性があります。Bean のスコープとライフサイクルはどちらもアプリケーションのパフォーマンス、安定性、保守性にとって重要です。

この記事では、Bean の範囲とライフサイクルについて詳しく説明し、それらが Spring アプリケーションに与える影響について説明します。読者が簡単に理解し、自分の開発実践に適用できるように、これらの概念をシンプルかつ明確な言葉で説明するよう努めます。

1. Beanのスコープ

Bean スコープという用語を初めて見ると混乱するかもしれませんが、問題はありません。次の簡単なケースで Bean スコープとは何かがわかります。

1.1 変更されたBeanケース

ユーザー A と B が使用できるパブリック Bean オブジェクトがありますが、A がこの Bean オブジェクトを使用すると、この Bean のデータが静かに変更されるため、予期しない結果が発生したときにユーザー B がこの Bean オブジェクトを使用することになりますか?

アノテーション を介してSpring コンテナにオブジェクトを格納するUserBean機能を持つクラスを作成します。このクラスには、および属性が含まれます@BeanUserUseridname

@Component
public class UserBeans {
    
    
    @Bean
    public User getUser(){
    
    
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

次に、UserController1クラスを作成し、@Controllerアノテーションを介して Spring コンテナに格納し、それを使用して属性注解Spring コンテナ内の Bean オブジェクトを取得します。さらに、Bean オブジェクトを指すprintUser一時参照を作成し、Bean のデータを変更するメソッドを作成します。myUser

@Controller
public class UserController1 {
    
    

    @Autowired
    private User user;

    public void printUser(){
    
    
        System.out.println("user: " +  user);

        User myUser = user;

        myUser.setName("李四");
        System.out.println("myUser: " + myUser);
        System.out.println("user: " +  user);
    }
}

もう 1 つ作成しUserController2@Controllerアノテーションを使用して Spring コンテナに格納し、それを使用して属性注解Spring コンテナ内の Bean オブジェクトを取得します。さらに、printUser取得した Bean オブジェクトを出力するだけのメソッドを作成します。

@Controller
public class UserController2 {
    
    

	@Resource
    private User user;
    
    public void printUser(){
    
    
        System.out.println("UserController2: user -> " +  user);
    }
}

スタートアップ クラスのメソッドは、mainをそれぞれApplicationContext取得し、それぞれのメソッドを実行して、Bean オブジェクトの変更による影響を観察します。UserController1UserController2

public static void main(String[] args) {
    
    
   ApplicationContext context
           = new ClassPathXmlApplicationContext("spring-config.xml");

   UserController1 userController1
           = context.getBean("userController1", UserController1.class);

   UserController2 userController2
           = context.getBean("userController2", UserController2.class);

   userController1.printUser();
   System.out.println("=========");
   userController2.printUser();
}

実行結果は以下の通りです。

操作の結果より、UserController1Beanのデータが変更されると、UserController2その時点で取得したBeanのデータも変更されます。つまり、Beanのコピーは1つだけであることになります。 Spring に保存されており、シングルトン モードです。したがって、Spring コンテナ内の Bean のデフォルトのスコープはシングルトン モード (singleton) です

1.2 範囲の定義

スコープ (Scope) は、プログラム内でアクセスできる変数または識別子の範囲を記述するためにプログラミングで使用されます。つまり、変数をどの部分で参照・使用できるかを指定しますスコープは、プログラマーが名前の競合を回避し、コード内の変数のライフサイクルを理解するのに役立つため、重要な概念です

Spring フレームワークでは、スコープ (Scope) を使用して Bean オブジェクトのライフサイクルと可視性のルールを定義します作用域决定了在不同的上下文环境中,Spring 容器如何管理和提供 Bean 对象たとえば、Bean を定義するときに、そのスコープを指定して、アプリケーション内での動作を決定できます。

1.3 Bean の 6 つのスコープ

Spring コンテナは Bean インスタンスを初期化するときにインスタンスのスコープも指定しますが、指定するスコープを変更しない場合、Spring はデフォルトでデフォルトのスコープを指定します。Spring の 6 つのスコープは次のとおりです。そのうちの最後の 4 つは Spring MVC に基づいて有効であるため、この記事では最初に説明しません。

  1. シングルトン スコープ (singleton)默认 : Spring コンテナーのスコープです。アプリケーションの存続期間中、このタイプの Bean のインスタンスは 1 つだけ作成され、Bean へのすべての参照は同じオブジェクトを実行しますこのスコープが適用されるステートレスでスレッドセーフな Bean オブジェクト (ユーティリティ クラス、構成クラスなど)。

  2. プロトタイプ スコープ (プロトタイプ) : Bean オブジェクトがリクエストされるたびに、Spring コンテナは新しい Bean インスタンスを作成するため、リクエストごとに異なるオブジェクト インスタンスが取得されます。プロトタイプ スコープは、一部のセッション関連 Bean など、より多くの状態を持ち、頻繁に作成および破棄する必要があるオブジェクトに適しています。

  3. リクエスト スコープ (リクエスト) : リクエスト スコープは、Web アプリケーションで一般的に使用されるスコープです。これは、各 HTTP リクエストが新しい Bean インスタンス を作成することを意味します。この Bean インスタンスは、現在のリクエストの処理中にのみ有効です。リクエストが終了すると、Bean は破棄されます。このようなスコープは通常、単一のリクエストに関連するデータを保存および処理するために使用されます。

  4. セッションスコープ(session) : セッションスコープは、Webアプリケーションにおけるユーザーセッションに基づくスコープです。各 HTTP セッション (Session) は Bean インスタンス に対応し、Bean インスタンスはセッション全体の存続期間中有効です。このスコープは、ユーザーのログイン情報など、セッション全体で状態を維持する必要があるオブジェクトに適しています。

  5. グローバル スコープ (アプリケーション) : グローバル スコープとは、Web アプリケーション全体のライフ サイクル中に Bean インスタンスが 1 つだけ作成されることを意味します。このスコープの Bean はアプリケーション全体で表示され、アプリケーション全体で共有する必要がある状態情報に適しています。

  6. HTTP WebSocket スコープ (websocket) : HTTP WebSocket スコープは、WebSocket 接続に基づくスコープです。各 WebSocket 接続は Bean インスタンスに対応し、WebSocket 接続の有効期間全体にわたって有効です。

シングルトン スコープ (シングルトン) とグローバル スコープ (アプリケーション) の違い:

シングルトン スコープとグローバル スコープは両方とも 1 つの Bean オブジェクトのみを作成しますが、両者の違いは何でしょうか?

1. 場所を定義します。

  • シングルトン スコープは Spring Core の一部であり、Spring IoC コンテナ全体で有効になります。Web アプリケーションだけでなく、あらゆる種類の Spring アプリケーションで動作します。
  • グローバル スコープは Spring Web の一部であり、サーブレット コンテナで有効になります。これは Web アプリケーション専用に設計されており、Spring Web ライブラリを通じてサポートされています。

2. 行動範囲:

  • シングルトン スコープは、Spring IoC コンテナ内に各 Bean のインスタンスが 1 つだけ存在することを保証するだけです。アプリケーション全体に、異なる Spring IoC コンテナの異なるインスタンスが存在する場合があります。
  • グローバル スコープにより、Web アプリケーション全体で各 Bean のインスタンスが 1 つだけ存在することが保証されます。同じサーブレット コンテナ内であっても、異なるサーブレット コンテナ内であっても、インスタンスは 1 つだけ存在します。

3. アプリケーションシナリオ:

  • シングルトン スコープは、ツール クラス、構成クラスなどのステートレスでスレッドセーフな Bean に適しています。シングルトン Bean にはアプリケーション全体でインスタンスが 1 つだけあるため、パフォーマンスとリソース使用率の点で一定の利点があります。
  • グローバル スコープは、グローバル構成オブジェクト、グローバル キャッシュなど、Web アプリケーション全体で状態情報を共有する必要がある Bean に適しています。グローバル スコープを使用すると、これらのオブジェクトがアプリケーション全体で 1 つのインスタンスのみを持ち、複数のサーブレット コンテナの影響を受けないようにすることができます。

4. コンテナを管理します。

  • シングルトン スコープの管理は Spring IoC コンテナが担当します。Spring IoC コンテナは Spring Core が提供する IoC コンテナによって管理され、Web アプリケーションとは関係ありません。
  • グローバルスコープの管理は、Webアプリケーションのライフサイクルに関わるSpring Webライブラリが提供するServletコンテナで管理する必要がある。

要約すると、シングルトン スコープは Spring IoC コンテナ全体に適用され、各 Bean がコンテナ内に 1 つのインスタンスのみを持つことを保証しますが、グローバル スコープは Web アプリケーション全体に適用され、各 Bean がアプリケーション全体で 1 つのインスタンスのみを持つことを保証します。適切なスコープの選択は、特定のアプリケーション要件と設計上の考慮事項によって異なります。

1.4 Beanスコープの設定

Spring では、Bean スコープを設定する方法が 2 つありますXML 配置和注解配置

1. XML 設定
XML 設定ファイルのタグ内で<bean>scope属性を使用して Bean のスコープを設定できます。たとえば、プロトタイプ スコープを持つ Bean の場合、次のように設定できます。

<bean id="myBean" class="com.spring.demo.MyBean" scope="prototype">
    <!-- Bean 的属性配置 -->
</bean>

その中には、scopeプロトタイプ、シングルトンなどのスコープの名前が指定されています。

2. アノテーションの設定

注釈を使用して Bean のスコープを構成すると、より簡潔になります。Spring では、@Scope先ほどクラスのプロトタイプ スコープを指定するなど、アノテーションを使用して Bean のスコープを指定できますUserBeans

@Component
public class UserBeans {
    
    
    @Bean(name = {
    
    "user1"})
    // @Scope("prototype") // 原型模式
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 使用全局变量设置
    public User getUser(){
    
    
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

この時点で、@Scopeのパラメータはプロトタイプなどのスコープの名前にすることができますが、ConfigurableBeanFactoryクラスで指定することもできます。ConfigurableBeanFactoryクラスのソースコード:


ConfigurableBeanFactoryクラスはドメイン名のカプセル化でもあることがわかります。

スコープがプロトタイプ モデルに設定されたら、Bean コンテンツを変更したコードを再度実行します。

UserControllerこの時点で、取得したものはどちらも真新しいBeanオブジェクトであり、一方を変更しても他方には影響がないことがわかります。

2. Springの実行プロセスとBeanのライフサイクル

2.1 Springの実行プロセス

春の実行フロー:

  1. Spring コンテナを開始します。

    • Spring コンテナが起動すると、構成ファイルを読み取るか、アノテーションをスキャンして Bean の定義情報を取得します。
  2. Bean をインスタンス化します (メモリ領域を最初から割り当てます):

    • Springコンテナは、構成情報やアノテーション定義に従ってBeanオブジェクトをインスタンス化する。インスタンス化プロセスには、コンストラクターの呼び出しと依存オブジェクトの作成が含まれる場合があります。
  3. SpringコンテナへのBean登録(ストレージ操作):

    • インスタンス化された Bean は Spring コンテナに登録され、コンテナはそれを管理スコープに取り込み、各 Bean に一意の識別子 (通常は Bean の名前または ID) を割り当てます。
  4. Beanの初期化(初期化操作):

    • init-method初期化メソッドが Bean 定義で構成されている場合 (属性やアノテーションを使用するなど@PostConstruct)、Spring コンテナはインスタンス化後に初期化メソッドを呼び出し、いくつかの初期化ロジックを実行します。
  5. Bean を必要なクラスにアセンブルします (操作を実行します)。

    • 他のクラスが Bean を使用する必要がある場合、Spring コンテナは依存関係注入設定に従って、必要なクラスに対応する Bean を自動的に注入します。このようにして、他のクラスは、Bean オブジェクトの作成および管理プロセスを気にせずに、Bean インスタンスを直接使用できます。
  6. Bean を使用します。

    • Bean が必要なクラスにアセンブルされたので、他のクラスで直接使用できるようになります。
  7. Bean の破棄 (破棄操作):

    • Bean 定義が破棄メソッド (destroy-method属性や@PreDestroyアノテーションの使用など) で構成されている場合、Spring コンテナーは、コンテナーが閉じられたときに破棄メソッドを呼び出し、クリーンアップ操作を実行します。

Bean のライフサイクルは Spring コンテナの制御下にあり、開発者は Bean の作成と破棄のプロセスを手動で管理する必要がないことに注意する必要があります。これが Spring IoC の中心的なアイデアです (反転)コントロール)。依存関係の注入により、オブジェクトの作成や管理を気にすることなく、ビジネス ロジックの実現に集中できるようになります。

2.2 Bean のライフサイクル

Bean のライフサイクルとは、Bean インスタンスの作成から破棄までのプロセス全体を指します。Spring コンテナでは、Bean のライフサイクルには主に次の段階が含まれます。

  1. インスタンス化: この段階では、Spring コンテナは構成情報とアノテーションに基づいて Bean インスタンスを作成します。これは「最初から」のプロセスです。つまり、メモリ領域を割り当て、コンストラクタを呼び出して Bean インスタンスを作成します。

  2. プロパティの設定 (Bean インジェクションとアセンブリ) : インスタンス化後、Spring コンテナは設定ファイルまたはアノテーションに従ってプロパティ値を Bean インスタンスに注入します。これは、プロパティや構築メソッドを通じてBeanのプロパティ値を設定する依存性注入(Dependency Injection)の処理です。

  3. Bean の初期化: 属性の割り当てが完了すると、Spring コンテナは次の手順を実行して Bean を初期化します。

    • さまざまな通知BeanNameAware: Bean が対応する Aware インターフェースを実装している場合、Spring はコールバック メソッド (たとえば、BeanFactoryAwareApplicationContextAwareなど)を通じて対応する状態を Bean に通知します。
    • 事前初期化メソッド:BeanPostProcessor事前初期化メソッドを実行しますpostProcessBeforeInitialization
    • 初期化メソッド (XML モードおよびアノテーション モード) : Bean が初期化メソッド (@PostConstructアノテーションまたはInitializingBeanインターフェイスの実装) を定義している場合、Spring コンテナはプロパティを設定した後、さらに初期化するためにこのメソッドを呼び出します。
    • 初期化後メソッド: Bean がBeanPostProcessorインターフェース実装クラスのメソッドなど、初期化後メソッドを定義している場合postProcessAfterInitialization、Bean の初期化後に Spring コンテナはこのメソッドを呼び出します。
  4. Bean の使用: 初期化が完了すると、アプリケーションで Bean インスタンスを使用できるようになります。これは他のクラスに挿入されるか、Spring コンテナを通じてそのメソッドを取得して呼び出します。

  5. Bean オブジェクトを破棄する: コンテナが閉じられるかプログラムが終了すると、Spring コンテナは次の手順を実行して Bean を破棄します。

    • Destroy pre-method: Bean が destroy-method を定義している場合、Spring コンテナは Bean が破棄される前にこのメソッドを呼び出します。
    • 破棄メソッド (XML モードおよびアノテーション モード): Bean が破棄メソッド (@PreDestroyアノテーションまたはDisposableBeanインターフェイスを実装) を定義している場合、Spring コンテナは Bean の破棄時にクリーニングのためにこのメソッドを呼び出します。

2.3 Bean のライフサイクルのデモンストレーション

次のコードは、完全な Bean ライフサイクル プロセスを示し、Spring の Bean の各ステージのコールバック メソッドを示しています。Bean ライフサイクルの実行フローを見てみましょう。


@Component
public class BeanComponent implements BeanNameAware, BeanPostProcessor {
    
    
    @Override
    public void setBeanName(String s) {
    
    
        System.out.println("执行了通知,Bean name -> " + s);
    }

    // xml 的初始化方法
    public void myInit(){
    
    
        System.out.println("XML 方式初始化");
    }

    @PostConstruct
    public void doPostConstruct(){
    
    
        System.out.println("注解 的初始化方法");
    }

    public void sayHi(){
    
    
        System.out.println("do sayHi()");
    }

    @PreDestroy
    public void preDestroy(){
    
    
        System.out.println("do PreDestroy");
    }

    // 前置方法
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("do postProcessBeforeInitialization");
        return bean;
    }

    // 后置方法
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("do postProcessAfterInitialization");
        return bean;
    }
}

spring-config.xmlまず、次の内容を構成ファイルに追加する必要があります。

<!-- XML 配置方式 -->
    <bean id="beanComponent"
          class="com.spring.demo.component.BeanComponent" 
          scope="prototype" 
          init-method="myInit" ></bean>

Bean のライフサイクルをシミュレートします。

  1. Bean をインスタンス化します。

    • Spring コンテナは構成ファイルを読み取るか、アノテーションをスキャンし、BeanComponentBean の定義を見つけてインスタンス化します。この時点では、Bean のコンストラクターは呼び出されていません。
  2. プロパティを設定します (Bean インジェクションとアセンブリ):

    • 必要に応じて、Spring コンテナは対応するプロパティをBeanComponentインスタンスに挿入します。
  3. Beanの初期化:

    • さまざまな通知を実行します。インターフェイスBeanComponentを実装しているため、メソッドが呼び出され、出力が表示されますBeanNameAwaresetBeanName执行了通知,Bean name -> beanComponent
    • 事前初期化メソッド: XML 構成メソッドの場合、myInitメソッドが呼び出され、出力が表示されますXML 方式初始化
    • 初期化メソッド (@PostConstruct アノテーション):doPostConstructメソッドが呼び出され、出力が表示されます注解 的初始化方法
    • 初期化後のメソッド:postProcessAfterInitializationメソッドが呼び出され、出力が表示されますdo postProcessAfterInitialization
  4. Bean を使用します。

    • 初期化後は、メソッドの呼び出しや出力の出力BeanComponentなど、アプリケーションでインスタンスを使用できるようになりますsayHi()do sayHi()
  5. Bean オブジェクトを破棄します。

    • コンテナが閉じられるか、プログラムが終了すると、preDestroyメソッドが呼び出され、出力が表示されますdo PreDestroy

上記の例では、BeanPostProcessorpre メソッドと post メソッドを実装するためにインターフェイスが使用されていることに注意してください。このインターフェースは、Bean の初期化の前後に Bean に対して追加の処理を実行する機能を提供します。実際のアプリケーションでは、BeanPostProcessorAOP プロキシ生成などのインターフェイスを実装することで、一部の特定の処理ロジックをカスタマイズできます。

クラスを開始するメソッドmain:

    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanComponent beanComponent= context.getBean("beanComponent", BeanComponent.class);
        beanComponent.sayHi();
    }

実行結果:

ここでは 2 つの通知が実行されていることがわかります。1 回目は通常、Spring コンテキストの作成時に Bean のインスタンス化が実行されるためです。2 回目の実行は、通常、プロトタイプ モードが使用され、実行によってgetBean新しいBeanオブジェクト。

おすすめ

転載: blog.csdn.net/qq_61635026/article/details/132080962