[Spring トピック] Spring の Bean ライフ サイクル ソース コード分析 - フェーズ 4 (Bean 破壊) (拡張して理解するだけ)

序文

ここで話しているのは、Bean の破棄プロセスです。おそらく、多くの友人が Beans の破壊について話すとき、ガベージ コレクションを思い浮かべるかもしれません。どちらもライフサイクルの最後の部分を実行しますが、実際には同じものではありません。ガベージ コレクションは JVM レベルのものであり、ここで言及されている Bean の破棄は Spring のものです。, したがって、もちろん同じものではありません。

読書のアドバイス

このレッスンの内容は、次のコードをエントリ ポイントとして説明します。

		// 注册Bean的销毁接口
		try {
    
    
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
    
    
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;

このコードは実際には Spring インスタンス化のメソッド内にありますAbstractAutowireCapableBeanFactory#doCreateBean()また、このメソッドの名前は誰もが知っていますが、このステップは単に破壊ロジックを登録するだけであり、実際の破壊ではありません。特定の条件が満たされた場合にのみ破壊されます。
registerDisposableBeanIfNecessary具体的なコードは次のとおりです。

  /**
     * 将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。 
     * 参数: 
     * beanName—bean的名称—bean实例mbd—bean的bean定义 
     * 参见: 
     * RootBeanDefinition。isSingleton RootBeanDefinition。getDependsOn, registerDisposableBean, registerDependentBean
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    
    
        AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
    
    
            if (mbd.isSingleton()) {
    
    
                // Register a DisposableBean implementation that performs all destruction
                // work for the given bean: DestructionAwareBeanPostProcessors,
                // DisposableBean interface, custom destroy method.
                registerDisposableBean(beanName, new DisposableBeanAdapter(
                        bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
            } else {
    
    
                // A bean with a custom scope...
                Scope scope = this.scopes.get(mbd.getScope());
                if (scope == null) {
    
    
                    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
                }
                scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
                        bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
            }
        }
    }

授業内容

1. Bean はいつ破棄されますか?

Bean の破棄は、Spring コンテナのシャットダウン中に発生します。この時点で、Spring のすべてのシングルトン Bean が破棄され、カスタム破棄ロジックを実装する Bean の破棄メソッドが実行されます。この記事で紹介するのは、カスタム Bean 破棄ロジックの実装方法です。
Spring コンテナが閉じている場合、次のように閉じた状態で表示できます。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

// 容器关闭
context.close();

または、シャットダウン フックを登録します。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 注册关闭钩子
context.registerShutdownHook();

Object newUser = context.getBean("user");
System.out.println(newUser);

[注: プロセスを強制的に強制終了 (kill pid) しても、カスタムBean 破棄ロジックは呼び出されません]

Spring がコンテナを閉じるプロセス:

  1. まずContextClosedEventイベントを発行します。
  2. lifecycleProcessor の onCloese() メソッドを呼び出します。
  3. シングルトン Bean を破棄する
    • disposableBean の反復処理
      • 各 disposableBean をシングルトン プールから削除します
      • disposableBean の destroy() を呼び出します。
      • この disposableBean が他の Bean にも依存している場合は、他の Bean も破棄する必要があります
      • この disposableBean に内部 Bean も含まれている場合は、これらの Bean をシングルトン プールから削除します
    • ユーザーが手動で登録したシングルトン Bean の beanName を格納するセットである、manualSingletonNames をクリアします。
    • すべての BeanNamesByType をクリアします。これはマップであり、キーは Bean タイプで、値はこのタイプのすべての BeanName の配列です。
    • 空の singletonBeanNamesByType。シングルトン Bean のみが保存される点を除き、allBeanNamesByType と同様です。

2. カスタム Bean 破棄ロジックを実装する

実装方法は以下のとおりです。

2.1 DisposableBean または AutoCloseable インターフェイスの実装

カスタム破棄が必要な Bean コードの例: (実装元: DisposableBean)

@Component
public class TestDestroyBean implements DisposableBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @Override
    public void destroy() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

または: (AutoCloseable から実装)

@Component
public class TestDestroyBean implements AutoCloseable {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }
    
    @Override
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.2 @PreDestroy アノテーションの使用

実装方法は 3 つあります。

@Component
public class TestDestroyBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @PreDestroy
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.3 その他の方法(破棄方法名を手動で指定)

もちろん、次のような他の方法もあります。

<bean destroy-method='xxx'>

または:

@Bean(destroyMethod = "xxx")

または、beanDefinition破棄方法を直接指定します。

@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
    
    
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    
    
        if (beanName.equals("user")) {
    
    
            beanDefinition.setInitMethodName("myInit");
            beanDefinition.setDestroyMethodName("xxxx");
        }
    }
}

上記の 3 つのメソッドは手動で指定されるため、特別な場所にあり、特別な値を設定できます(inferred)
破棄メソッドの名前がこれに設定され、Bean が実装されていない場合、破棄プロセス中に Bean の下にメソッドがあるDisposableBeanどうかがチェックされます「はい」の場合、自動的に【ユーザー定義の破壊方法】にバインドされます。closeshutdown

3. Beanの登録・破棄までの手順と方法の詳細説明

この破棄プロセスには【3つのコアクラス、6つのコアメソッド】が含まれます。

3.1 AbstractBeanFactory#requiresDestruction: 破棄する必要がありますか?

メソッド呼び出しチェーン: エントリから: registerDisposableBeanIfNecessary() 呼び出し (フルパス)
: org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction
メソッド アノテーション: 指定された Bean をこのファクトリ内の使い捨て Bean のリストに追加し、その DisposableBean インターフェイスを登録しますおよび/またはファクトリが閉じられたときに呼び出す指定された destroy メソッド (該当する場合)。シングルトンにのみ適用されます。

ソースコードは次のとおりです。

protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    
    
    return (
            bean.getClass() != NullBean.class && (
                    DisposableBeanAdapter.hasDestroyMethod(bean, mbd) 
                    || (hasDestructionAwareBeanPostProcessors() 
                            && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))
            )
    );
}

メソッドの解釈: 内部の主要なソース コードは、実際には 2 つのステップに分かれています。次のように:

  1. 指定された破棄メソッドはありますか。DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
  2. DestructionAwareBeanPostProcessor破壊を感知できる Bean ポストプロセッサがあるかどうか( hasDestructionAwareBeanPostProcessors)。ある場合はトラバースしますDisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware)

3.2 DisposableBeanAdapter.hasDestroyMethod: 破棄メソッドの有無

メソッド呼び出しチェーン: 3.1 の requireDestruction() によって呼び出されます。
フルパス: org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod
メソッド アノテーション: 指定された Bean に呼び出す破棄メソッドがあるかどうかを確認します。

ソースコードは次のとおりです。

public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
    
    
	return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}

実際の内容は非常にシンプルで、手順は次のとおりです。

  1. 現在の Bean が DisposableBean インターフェースを実装しているかどうか
  2. そうでない場合は、inferDestroyMethodIfNecessary推論された破棄メソッドを呼び出します (後述)。

3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary: 破棄メソッドを推測する

メソッド呼び出しチェーン: 3.2 の hasDestroyMethod() によって呼び出されます
。 フルパス: org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary
メソッド コメント:
指定された beanDefinition の「destroyMethodName」属性の現在値が AbstractBeanDefinition の場合。次に、destroy メソッドを推論してみます。候補メソッドは現在、「close」または「shutdown」という名前のパブリック パラメータなしメソッド (ローカルで宣言または継承されたもの) に限定されています。そのようなメソッドが見つからない場合は、指定された BeanDefinition の「destroyMethodName」を null に更新します。見つからない場合は、推論されたメソッドの名前に設定されます。この定数は @Bean#destroyMethod 属性のデフォルト値として使用され、この定数の値は XML または属性でも使用できます。また、java.io.Closeable および AutoCloseable インターフェースも処理し、Bean の実装時に反射的に「close」メソッドを呼び出します。

ソースコードは次のとおりです。

	@Nullable
	private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
    
    
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		if (destroyMethodName == null) {
    
    
			destroyMethodName = beanDefinition.getDestroyMethodName();
			boolean autoCloseable = (bean instanceof AutoCloseable);
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && autoCloseable)) {
    
    

				// 当销毁方法名字等于"(inferred)",且bean不是DisposableBean实现类
				destroyMethodName = null;
				if (!(bean instanceof DisposableBean)) {
    
    
					if (autoCloseable) {
    
    
						destroyMethodName = CLOSE_METHOD_NAME;
					}
					else {
    
    
						try {
    
    
							destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
						}
						catch (NoSuchMethodException ex) {
    
    
							try {
    
    
								destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
							}
							catch (NoSuchMethodException ex2) {
    
    
								// no candidate destroy method found
							}
						}
					}
				}
			}
			beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
		}
		return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
	}

メソッドの解釈: 解釈する必要はありません。投稿を繰り返してコメントするだけです。destroy メソッドを推測してみます。候補メソッドは現在、「close」または「shutdown」という名前のパブリック パラメータなしメソッド (ローカルで宣言または継承されたもの) に限定されています。そのようなメソッドが見つからない場合は、指定された BeanDefinition の「destroyMethodName」を null に更新します。見つからない場合は、推論されたメソッドの名前に設定されます。この定数は @Bean#destroyMethod 属性のデフォルト値として使用され、この定数の値は XML の <bean destroy-method=""> または属性でも使用できます。また、java.io.Closeable および AutoCloseable インターフェースも処理し、Bean の実装時に反射的に「close」メソッドを呼び出します。

3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors: 知覚的破壊 Bean ポストプロセッサがあるかどうか

メソッド呼び出しチェーン: 3.1 の requireDestruction() によって呼び出されます。
フルパス: org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors
メソッドのコメント: ファクトリが destroyAwareBeanPostProcessor を保持しているかどうかを返します。これは、閉じられたときにシングルに適用されます。豆。

ソースコードは次のとおりです。

	protected boolean hasDestructionAwareBeanPostProcessors() {
    
    
		return !getBeanPostProcessorCache().destructionAware.isEmpty();
	}

3.5 DisposableBeanAdapter.hasApplicableProcessors: 現在の Bean に適用される破棄対応 Bean ポストプロセッサがあるかどうか

メソッド呼び出しチェーン: 3.1 の requireDestruction() によって呼び出されます。
フルパス: org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
メソッド アノテーション: 指定された Bean に破壊を認識するポストプロセッサが適用されているかどうかを確認します。

ソースコードは次のとおりです。

	public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
    
    
		if (!CollectionUtils.isEmpty(postProcessors)) {
    
    
			for (DestructionAwareBeanPostProcessor processor : postProcessors) {
    
    
				if (processor.requiresDestruction(bean)) {
    
    
					return true;
				}
			}
		}
		return false;
	}

古典的な BeanPostProcessor がこれに対処しているので、それについては触れません。

3.6 DefaultSingletonBeanRegistry#registerDisposableBean: 破棄する必要がある Bean を登録します

メソッド呼び出しチェーン: エントリから呼び出されます: registerDisposableBeanIfNecessary()
フルパス: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean
メソッド アノテーション: 指定された Bean をこのレジストリ内の破棄された Bean のリストに追加します。

ソースコードは次のとおりです。

private final Map<String, Object> disposableBeans = new LinkedHashMap<>();


public void registerDisposableBean(String beanName, DisposableBean bean) {
    
    
	synchronized (this.disposableBeans) {
    
    
		this.disposableBeans.put(beanName, bean);
	}
}

メソッドの解釈: いわゆる登録では、実際には現在の Bean といくつかの情報がキャッシュ マップに追加されます。使用する必要があるときは、マップを直接移動するだけです

3.7 Bean の登録と破棄のプロセスの概要

全体として、次の 2 つのステップがあります。

  1. シングルトン Bean なので、破棄する必要があるかどうかが判断されます。判定手順は以下の通りです。Spring の各バージョンの詳細は異なりますが、全体としては一貫しています
    • 現在のBeanがDisposableBeanインターフェースを実装しているかどうか、実装されている場合は直接trueを返し、そうでない場合は[破棄メソッドの推論]の処理に進みます。
    • 推測された破壊方法
      • BeanDefinition で destroyMethod が指定されているかどうか、およびdestroyMethod==(inferred)close「はい」の場合、現在の Bean の下にメソッドがあるかどうかを調べshutdown、「はい」の場合は、破棄メソッドの名前を直接返します。
      • または、現在の Bean が AutoCloseable インターフェースを実装しているかどうか、実装している場合は、破棄メソッドの名前を直接返します。
    • [推論された破壊方法]で結果がなかった場合は、[Aware破壊Beanポストプロセッサ]DestructionAwareBeanPostProcessor.requiresDestruction(bean)を呼び出して判断します。
      • ApplicationListenerDetector で直接、現在の Bean が ApplicationListener サブクラスである場合は、それを破棄する必要があります
      • InitDestroyAnnotationBeanPostProcessor により、@PreDestroy アノテーションを持つメソッドを破棄する必要があります
  2. 破棄する必要がある場合は、それを DisposableBeanAdapter オブジェクトに適応させ、disposableBeans (LinkedHashMap) に格納します。

4. Beanの登録と破棄のロジックフローチャート

ここに画像の説明を挿入

5.コンセプトレビュー

ここで使用される重要なポストプロセッサの 1 つは、InitDestroyAnnotationBeanPostProcessor次のように定義されているということです。

    /**
     *
     通用场景
     术语库
     beanpostprocessor实现,调用带注释的init和destroy方法。允许一个注释替代Spring的org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean回调接口。
     这个后处理器检查的实际注释类型可以通过“initAnnotationType”和“destroyAnnotationType”属性来配置。可以使用任何自定义注释,因为没有必需的注释属性。
     Init和destroy注释可以应用于任何可见性的方法:public、package-protected、protected或private。可以注释多个这样的方法,但建议分别只注释一个init方法和destroy方法。
     Spring的
     org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
     支持JSR-250开箱即用的javax.annotation.PostConstruct和javax.annotation.PreDestroy注释,
     就像init annotation和destroy annotation一样。此外,它还支持javax.annotation.Resource注释,
     用于注释驱动的命名bean注入。
     自:
     2.5
     参见:
     setInitAnnotationType, setDestroyAnnotationType
     作者:
     Juergen hoel
     以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景
     逐句对照
     */
     public class InitDestroyAnnotationBeanPostProcessor
		implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
    
    
}

要約する

  1. Bean の破棄プロセスを学習しました
  2. Bean の登録と破棄のロジックを学習しました
  3. Bean 破棄ロジックをカスタマイズする方法を学習しました

おすすめ

転載: blog.csdn.net/qq_32681589/article/details/132316780