[весенний исходный код серии-05] процесс выполнения метода prepareRefresh в обновлении

Общий столбец серии исходного кода Spring


содержание адрес ссылки
[1] Общий обзор исходного кода Spring https://blog.csdn.net/zhenghuishengq/article/details/130940885
[2] Проанализируйте общий процесс IOC с помощью метода обновления. https://blog.csdn.net/zhenghuishengq/article/details/131003428
[3] Предварительная работа по обновлению при запуске файла конфигурации xml spring https://blog.csdn.net/zhenghuishengq/article/details/131066637
[4] Предварительная работа по обновлению при запуске весны в режиме аннотации. https://blog.csdn.net/zhenghuishengq/article/details/131113249
[5] Процесс выполнения prepareRefresh при обновлении https://blog.csdn.net/zhenghuishengq/article/details/131186016

1. Углубленный анализ метода обновления prepareRefresh

В первых двух статьях говорилось о предварительной работе и подготовительной работе метода Refresh.Метод аннотации требует большей предварительной работы, чем метод xml. Следующим шагом является ввод самой важной части обновления.В предыдущих статьях также было примерно обобщено 12 методов обновления.В следующих статьях каждый метод будет подробно описан.

Посмотрите еще раз на метод Refresh, прежде всего, к этому методу будет добавлена ​​блокировка синхронизации 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. Следующим шагом является установка двух флагов: один — установить флаг для закрытия контейнера, а другой — установить флаг для активации контейнера.

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. В дополнение к тому, что слушатель пуст при запуске весны, событие Early 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 есть две конкретные реализации, вы можете сначала установить значение, а затем проверить установленное значение.

//设置相关的属性值
@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, получение объекта среды и установка значения свойства текущей системы в Environment. объект, создайте экземпляр объекта прослушивателя и объекта события и установите значение null .

Supongo que te gusta

Origin blog.csdn.net/zhenghuishengq/article/details/131186016
Recomendado
Clasificación