【Springソースコードシリーズ-05】リフレッシュ時のprepareRefreshメソッドの実行処理

Springソースコードシリーズの総合コラム


コンテンツ リンクアドレス
[1] Springソースコードの全体概要 https://blog.csdn.net/zhenghuishengq/article/details/130940885
[2] リフレッシュ手法によるIOCの全体プロセスの分析 https://blog.csdn.net/zhenghuishengq/article/details/131003428
[3] Spring起動時のXML設定ファイルのリフレッシュの前作業 https://blog.csdn.net/zhenghuishengq/article/details/131066637
[4] アノテーションモードでSpringを起動する際のリフレッシュの前作業 https://blog.csdn.net/zhenghuishengq/article/details/131113249
[5] リフレッシュ時のprepareRefreshの実行処理 https://blog.csdn.net/zhenghuishengq/article/details/131186016

1. リフレッシュの prepareRefresh メソッドの詳細な分析

最初の 2 つの記事では、refresh メソッドの事前作業と準備作業について説明しましたが、アノテーション メソッドには XML メソッドよりも多くの事前作業が必要です。次は最も重要なリフレッシュの部分に入りますが、前回の記事でもリフレッシュの12の方法を大まかにまとめましたが、次の記事ではそれぞれの方法について詳しくまとめていきます。

もう一度更新メソッドを見てください。まず、スレッドの安全性を確保するために、同期ロックsynchronized がこのメソッドに追加されます。

@Override
public void refresh() throws BeansException, IllegalStateException {
    
    
	synchronized (this.startupShutdownMonitor) {
    
    
		//1:准备刷新上下文环境
		prepareRefresh();
		//2:获取告诉子类初始化Bean工厂,不同工厂有不同的实现
        //  并且将配置文件的属性值加载到当前工厂中
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		//3:对bean工厂进行填充属性
		prepareBeanFactory(beanFactory);
		try {
    
    
			// 第四:留个子类去实现该接口,bean工厂的后置处理器
			postProcessBeanFactory(beanFactory);
			// 调用我们的bean工厂的后置处理器. 
       	 	//1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
			invokeBeanFactoryPostProcessors(beanFactory);
			// 注册我们bean的后置处理器
			registerBeanPostProcessors(beanFactory);
			// 初始化国际化资源处理器.
			initMessageSource();
			// 创建事件多播器
			initApplicationEventMulticaster();
			// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
			onRefresh();
			//把我们的事件监听器注册到多播器上
			registerListeners();
			// 实例化我们剩余的单实例bean.
			finishBeanFactoryInitialization(beanFactory);
			// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
			finishRefresh();
		}
    }
}

1. prepareRefresh()の具体的な実行手順

次は、この記事の焦点でもあるリフレッシュの最初のメソッドです: prepareRefresh()は、名前が示すとおり、リフレッシュ前の準備作業です。次に、このメソッドを入力して、詳細な内部実行プロセスを表示します。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-gB4ydeBy-1686631810191)(img/1686038023143.png)]

その主なコードスニペットは次のとおりです

protected void prepareRefresh() {
    
    
	// Switch to active.
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);
	/**
	 * 比如我们自己写一个类重写了initPropertySources方法,在该方法中设置了一个环境变量的值为A
	 * 启动的时候,我的环境变量中没有该值就会启动抛出异常
	 */
	initPropertySources();

	/**
	 * 用来校验我们容器启动必须依赖的环境变量的值
	 */
	getEnvironment().validateRequiredProperties();

	/**
	 * 创建一个早期事件监听器对象
	 */
	if (this.earlyApplicationListeners == null) {
    
    
		this.earlyApplicationListeners = new LinkedHashSet < > (this.applicationListeners);
	} else {
    
    
		this.applicationListeners.clear();
		this.applicationListeners.addAll(this.earlyApplicationListeners);
	}
	this.earlyApplicationEvents = new LinkedHashSet < > ();
}

1. まず、最初の方法は、このメソッドの実行時の開始時刻を記録し、最後にメソッドの終了時刻からこの時間を減算して、リフレッシュ メソッド全体の差を取得します。

this.startupDate = System.currentTimeMillis();   //容器启动的时间

2. 次のステップでは 2 つのフラグを設定します。1 つはコンテナを閉じるためのフラグを設定し、もう 1 つはコンテナをアクティブにするためのフラグを設定します。

this.closed.set(false);   //容器关闭的标志位
this.active.set(true);    //容器激活的标志位

3. 次に、重要なメソッドinitPropertySources()があります。このメソッドは空のメソッドであり、実装は主にサブクラスに任されています。以下に、このメソッドの機能を理解するための小さなデモを示します。

protected void initPropertySources() {
    
    
	// For subclasses: do nothing by default.
}

4. 次のステップでは、最初にシステム環境オブジェクトを取得します。StandardEnvironmentインスタンス オブジェクトは、更新メソッドに入る前に取得されるため、ここでの環境オブジェクトは空ではなく、オブジェクト値が直接返されます。このオブジェクトには、すべてのシステム変数とシステム プロパティが含まれています。

@Override
public ConfigurableEnvironment getEnvironment() {
    
    
	if (this.environment == null) {
    
    
		this.environment = createEnvironment();
	}
	return this.environment;
}

この標準環境オブジェクトを取得した後の次のステップは、このvalidateRequiredPropertiesを通じて内部のプロパティに対して検証操作を実行することです。

getEnvironment().validateRequiredProperties();

対応する検証操作は次のとおりです。これらの属性をループして属性がシステム変数に存在するかどうかを判断し、属性が存在しない場合は例外をスローします。

@Override
public void validateRequiredProperties() {
    
    
	MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
	for (String key: this.requiredProperties) {
    
    
        //判断当前属性是否存在set集合中,即是否为系统属性
		if (this.getProperty(key) == null) {
    
    
            //如果当前属性不是系统属性,则添加一个异常
			ex.addMissingRequiredProperty(key);
		}
	}
    //如果异常数量不为空,则抛出异常
	if (!ex.getMissingRequiredProperties().isEmpty()) {
    
    
		throw ex;
	}
}

このスローされた例外では、次のような文が出力されます

The following properties were declared as required but could not be resolved: 

5. 次のステップは、イベント リスナー オブジェクトを作成することです。Spring が起動すると、earlyApplicationListenersリスナーオブジェクトはデフォルトで空ですが、springboot では、最初にロードする必要がある spring.factories に多くのイベントがあるため、 springBoot オブジェクトは空ではありません。これは Spring に基づく拡張と同等です。したがって、earlyApplicationListenersオブジェクトが空かどうかを判断する必要がある。

if (this.earlyApplicationListeners == null) {
    
    
    //spring使用,默认为空
	this.earlyApplicationListeners = new LinkedHashSet <> (this.applicationListeners);
} else {
    
    
	// Reset local application listeners to pre-refresh state.
	//springboot使用,先清除原有的,再添加
    this.applicationListeners.clear();
	this.applicationListeners.addAll(this.earlyApplicationListeners);
}

たとえば、spring.factories ファイルでは、非常に多くのリスナーがデフォルトで挿入されます。この機能は主に springboot の拡張機構です

ここに画像の説明を挿入

6. 春の開始時にはリスナーが空であることに加えて、初期の Event イベントも空です。

this.earlyApplicationEvents = new LinkedHashSet<>();

2、initPropertySources (拡張可能)

initPropertySourcesメソッドは内部が空のメソッドであるため、主にサブクラスが実装するために予約されているため、ここでは次のようにClassPathXmlApplicationContextクラスを継承するクラスを定義できます。

/**
 * @author zhenghuisheng
 * @date : 2023/6/6
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
    
    
	
    //String... locations:xml配置文件的路径
	public MyClassPathXmlApplicationContext(String... locations) {
    
    
		super(locations);
	}

	@Override
	protected void initPropertySources() {
    
     / /初始化属性
		//设置属性
		getEnvironment().setRequiredProperties("USERNAME");
		//获取属性
		String requiredProperty = getEnvironment().getRequiredProperty("USERNAME");
		System.out.println("当前系统用户名称为:" + requiredProperty);
        //获取app名称
		String applicationName = getApplicationName();
		System.out.println("当前应用名称为:" + applicationName);
		//获取前置处理器
		List<BeanFactoryPostProcessor> list = getBeanFactoryPostProcessors();
		System.out.println("定义的bean工厂的后置处理器为的集合长度为:"+ list.size());
        ...
	}
}

前回の記事で述べたように、更新メソッドの前の事前作業では、次の図に示すように、すべてのシステム環境とシステム変数が取得されます。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-JQoMpucv-1686631810193)(img/1685947066781.png)]

したがって、これらの環境変数をここで直接取得できます。次に、テスト用のメインのスタートアップ クラスを作成します。

 public static void main(String[] args) {
    
    
    //扩展,用于鉴定属性
	ApplicationContext m = new MyClassPathXmlApplicationContext("classpath.a.xml");    
}

環境オブジェクトが取得できるので、この環境変数にプロパティを設定します

getEnvironment().setRequiredProperties("USERNAME");

上記で設定した属性USERNAME値は、設定後に検証操作を実行します。主に、その値がシステムに存在するかどうかを検証します。値が存在しない場合は、直接例外がスローされます。

void validateRequiredProperties() throws MissingRequiredPropertiesException;

開発中に、プログラムに特定の属性やプレフィックスなどの特定の値を含める必要がある場合、このステップで判断でき、アプリケーション層に入って判断する必要はありませんたとえば、抽象クラスAbstractEnvironmentには2 つの具象実装があり、最初に値を設定し、次に設定値を確認できます。

//设置相关的属性值
@Override
public void setRequiredProperties(String...requiredProperties) {
    
    
	this.propertyResolver.setRequiredProperties(requiredProperties);
}
//验证属性值,属性在在系统属性中不存在,则立马抛出异常
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    
    
	this.propertyResolver.validateRequiredProperties();
}

検証プロセスは次のとおりです:現在のシステム属性にこの値があるかどうかを判断し、システムにこの値がある場合は問題ありません。そのような値がない場合は、例外がスローされ、エラーが報告されます。

@Override
public void validateRequiredProperties() {
    
    
	MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
	for (String key: this.requiredProperties) {
    
    
        //判断当前系统属性有没有这个值
		if (this.getProperty(key) == null) {
    
    
			ex.addMissingRequiredProperty(key);
		}
	}
    //如果系统有这个值,则不管;没有这个值,则抛出异常报错
	if (!ex.getMissingRequiredProperties().isEmpty()) {
    
    
		throw ex;
	}
}

主な機能はパラメータの事前検証を行うことであり、実際の開発ではほとんど使用されません。

3. まとめ

この prepareRefresh メソッドでは、要約すると、コンテナーの起動時間を設定する、アクティブ状態を true に設定する、閉じた状態を false に設定する、環境オブジェクトを取得する、現在のシステムのプロパティ値を環境に設定する、という 5 つのことが行われます。オブジェクト、リスナー オブジェクトとイベント オブジェクトをインスタンス化し、 null に設定します

おすすめ

転載: blog.csdn.net/zhenghuishengq/article/details/131186016