[spring source code series-05] processus d'exécution de la méthode prepareRefresh dans refresh

La colonne globale de la série de codes sources Spring


contenu adresse du lien
[1] Aperçu général du code source de Spring https://blog.csdn.net/zhenghuishengq/article/details/130940885
[2] Analyser le processus global d'IOC via la méthode d'actualisation https://blog.csdn.net/zhenghuishengq/article/details/131003428
[3] Le pré-travail de rafraîchissement lorsque le fichier de configuration xml démarre au printemps https://blog.csdn.net/zhenghuishengq/article/details/131066637
[4] Le pré-travail de rafraîchissement lors du démarrage du printemps en mode annotation https://blog.csdn.net/zhenghuishengq/article/details/131113249
[5] Processus d'exécution de prepareRefresh dans refresh https://blog.csdn.net/zhenghuishengq/article/details/131186016

1. Analyse approfondie de la méthode de rafraîchissement prepareRefresh

Les deux premiers articles parlaient du pré-travail et du travail préparatoire de la méthode refresh, la méthode d'annotation nécessite plus de pré-travail que la méthode xml. L'étape suivante consiste à entrer dans la partie la plus importante de l'actualisation. Les articles précédents résument également approximativement les 12 méthodes d'actualisation. Dans les articles suivants, chaque méthode sera résumée en détail.

Regardez à nouveau la méthode de rafraîchissement, tout d'abord, un verrou de synchronisation synchronized sera ajouté à cette méthode pour assurer la sécurité du thread

@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. Le processus d'exécution spécifique de prepareRefresh()

Vient ensuite la première méthode d'actualisation, qui fait également l'objet de cet article : prepareRefresh() , comme son nom l'indique, est le travail de préparation avant l'actualisation. Ensuite, entrez cette méthode pour afficher le processus d'exécution interne détaillé

[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme de lien antivol, il est recommandé d'enregistrer l'image et de la télécharger directement (img-gB4ydeBy-1686631810191)(img/1686038023143.png)]

Son extrait de code principal est le suivant

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. Tout d'abord, la première consiste à enregistrer l'heure de début lors de l'exécution de cette méthode, et enfin à soustraire cette heure de l'heure à laquelle la méthode se termine pour obtenir la différence de l'ensemble de la méthode d'actualisation.

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

2. L'étape suivante consiste à définir deux indicateurs, l'un consiste à définir l'indicateur de fermeture du conteneur et l'autre à définir l'indicateur d'activation du conteneur.

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

3. Ensuite, il y a une méthode importante initPropertySources() .Cette méthode est une méthode vide, qui est principalement laissée aux sous-classes à implémenter.Ce qui suit donnera une petite démo pour comprendre la fonction de cette méthode.

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

4. L'étape suivante consiste à obtenir d'abord l'objet d'environnement système, l'objet d'instance StandardEnvironment obtenu avant d'entrer dans la méthode d'actualisation , de sorte que l'objet d'environnement ici n'est pas vide et la valeur de l'objet est renvoyée directement. Cet objet contient toutes les variables système et les propriétés système.

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

Après avoir obtenu cet objet d'environnement standard, l'étape suivante consiste à effectuer une opération de validation sur les propriétés à l'intérieur via ce validateRequiredProperties

getEnvironment().validateRequiredProperties();

L'opération de vérification correspondante est la suivante, elle parcourt ces attributs pour déterminer si l'attribut existe dans la variable système et lève une exception s'il n'existe pas

@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;
	}
}

Dans cette exception levée, une telle phrase est imprimée

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

5. L'étape suivante consiste à créer un objet écouteur d'événement.Lorsque le printemps démarre, l' objet écouteur earlyApplicationListeners est vide par défaut, mais dans springboot, car il y a beaucoup d'événements dans spring.factories qui doivent être chargés en premier, donc dans springBoot L'objet n'est pas vide, ce qui équivaut à une amélioration sur la base de spring. Par conséquent, il est nécessaire de déterminer si l'objet earlyApplicationListeners est vide.

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);
}

Par exemple, dans le fichier spring.factories, de nombreux écouteurs sont injectés par défaut. Cette fonction est principalement un mécanisme d'extension de springboot

insérez la description de l'image ici

6. Outre le fait que l'écouteur est vide au début du printemps, l'événement Early Event est également vide.

this.earlyApplicationEvents = new LinkedHashSet<>();

2, initPropertySources (extensible)

Étant donné que la méthode initPropertySources est une méthode vide à l'intérieur, elle est principalement réservée aux sous-classes à implémenter, donc ici, vous pouvez définir une classe qui hérite de la classe ClassPathXmlApplicationContext , comme suit

/**
 * @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());
        ...
	}
}

Comme mentionné dans l'article précédent, dans le pré-travail avant la méthode d'actualisation, toutes les variables d'environnement système et système seront obtenues, comme indiqué dans la figure suivante, les variables d'environnement système

[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-JQoMpucv-1686631810193)(img/1685947066781.png)]

Vous pouvez donc obtenir ces variables d'environnement directement ici. Ensuite, écrivez une classe de démarrage principale pour les tests

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

Étant donné que l'objet d'environnement peut être obtenu, définissez une propriété dans cette variable d'environnement

getEnvironment().setRequiredProperties("USERNAME");

La valeur de l'attribut USERNAME définie ci-dessus effectuera une opération de vérification après la définition, principalement pour vérifier si la valeur existe dans le système, et si elle n'existe pas, une exception sera levée directement.

void validateRequiredProperties() throws MissingRequiredPropertiesException;

Au cours du développement, si le programme doit contenir une certaine valeur, telle qu'un certain attribut ou un certain préfixe, il peut être jugé à cette étape, et il n'est pas nécessaire d'entrer dans la couche application pour juger . Par exemple, dans la classe abstraite AbstractEnvironment , il existe deux implémentations concrètes, vous pouvez d'abord définir la valeur, puis vérifier la valeur définie.

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

Le processus de vérification est le suivant : jugez si l'attribut système actuel a cette valeur, si le système a cette valeur, cela n'a pas d'importance ; s'il n'y a pas une telle valeur, une exception est levée et une erreur est signalée

@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;
	}
}

La fonction principale est de faire une vérification des paramètres à l'avance, et elle est rarement utilisée dans le développement réel.

3. Résumé

Dans cette méthode prepareRefresh, en résumé, cinq choses sont faites : définir l'heure de démarrage du conteneur, définir l'état actif sur true, définir l'état fermé sur false, obtenir l'objet d'environnement et définir la valeur de la propriété du système actuel sur Environment. objet, instancier un objet écouteur et un objet événement, et défini sur null .

Je suppose que tu aimes

Origine blog.csdn.net/zhenghuishengq/article/details/131186016
conseillé
Classement