La troisième partie de la trilogie d'apprentissage de la source d'initialisation Spring 4.1.8: AbstractApplicationContext.refresh

Ce chapitre est la dernière partie de la série "trilogie d'apprentissage du code source d'initialisation spring4.1.8", se concentrant sur l'apprentissage de la méthode refresh () de la classe AbstractApplicationContext;

Examinons d'abord le code d'initialisation de la classe ClassPathXmlApplicationContext comme suit:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {
	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}

Les deux premiers de la trilogie ont appris super (parent) et setConfigLocations (configLocations) :

  1. " L'une des trilogies de l'apprentissage du code source d'initialisation du printemps 4.1.8: méthode de construction AbstractApplicationContext ";

  2. " Spring 4.1.8 Initialization Source Learning Trilogy Part Two: setConfigLocations Method ";

Introduction à la méthode de rafraîchissement

Dans ce chapitre, nous découvrirons la méthode d'actualisation. Le code source spécifique se trouve dans la classe AbstractApplicationContext. Pour une introduction à cette méthode, consultez les commentaires dans le code source suivant:

@Override
public void refresh() throws BeansException, IllegalStateException {
	//startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到,确保刷新和销毁不会同时执行
	synchronized (this.startupShutdownMonitor) {
		// 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中
		prepareRefresh();


		// 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,
		// 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();


		// 对beanFactory做一些设置,例如类加载器、spel解析器、指定bean的某些类型的成员变量对应某些对象等
		prepareBeanFactory(beanFactory);


		try {
			// 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)
			postProcessBeanFactory(beanFactory);


			// 执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory后置处理器处理bean定义)
			invokeBeanFactoryPostProcessors(beanFactory);


			// 将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到
			registerBeanPostProcessors(beanFactory);


			// 初始化国际化服务
			initMessageSource();


			// 创建事件广播器
			initApplicationEventMulticaster();


			// 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作
			onRefresh();


			// 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册
			registerListeners();


			// 单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成
			finishBeanFactoryInitialization(beanFactory);


			// applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
			finishRefresh();
		}


		catch (BeansException ex) {
			logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);


			// 刷新失败后的处理,主要是将一些保存环境信息的集合做清理
			destroyBeans();


			// applicationContext是否已经激活的标志,设置为false
			cancelRefresh(ex);


			// Propagate exception to caller.
			throw ex;
		}
	}
}

Ensuite, analysez-le un par un:

Méthode prepareRefresh

Le code source de la méthode prepareRefresh est le suivant:

protected void prepareRefresh() {
	//记录初始化开始时间
	this.startupDate = System.currentTimeMillis();
	//context是否关闭的标志,设置为false
	this.closed.set(false);
	//context是否激活的标志,设置为true
	this.active.set(true);


	if (logger.isInfoEnabled()) {
		logger.info("Refreshing " + this);
	}


	//留给子类实现的空方法
	initPropertySources();


	/**
	AbstractPropertyResolver类的requiredProperties是个集合,
	在下面的validateRequiredProperties方法中,都要拿requiredProperties中的元素作为key去检查是否存在对应的环境变量,
	如果不存在就抛出异常
	*/
	getEnvironment().validateRequiredProperties();
}

Dans le code ci-dessus, faites attention aux deux points suivants:

  1. initPropertySources est une méthode vide, qui est laissée aux sous-classes. En prenant la classe AnnotationConfigWebApplicationContext comme exemple, la méthode initPropertySources est remplacée:

@Override
protected void initPropertySources() {
	ConfigurableEnvironment env = getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
	}
}

Suivi de la méthode initPropertySources ci-dessus, enfin trouvé WebApplicationContextUtils.initServletPropertySources:

public static void initServletPropertySources(
			MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {


		Assert.notNull(propertySources, "propertySources must not be null");
		if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
				propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
					new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
		}
		if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
				propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
					new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
		}
	}

Le code ci-dessus consiste à ajouter des données de variables d'environnement au contexte (les données proviennent des informations de configuration liées aux servlets), de sorte que l'environnement Spring puisse obtenir la variable correspondante à partir de la clé de contexte à tout moment;

  1. Le but de getEnvironment (). ValidateRequiredProperties () est de vérifier si "certaines" variables existent dans le contexte , qu'est-ce que "certaines"? Jetez un œil à la méthode validateRequiredProperties, recherchez plusieurs couches d'appels et enfin implémentez-la dans la méthode validateRequiredProperties de la classe AbstractPropertyResolver:

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

Le code ci-dessus montre que si le nom dans l'ensemble requiredProperties ne peut pas trouver la variable correspondante dans le contexte, une exception sera levée;

La question est donc de savoir quand les propriétés requises sont définies? Il n'y a pas d'appel dans Spring-Framework, mais le code source officiel du test unitaire nous a inspiré, comme indiqué ci-dessous:

Comme indiqué dans l'encadré rouge ci-dessus, si l'entreprise doit s'assurer que certaines variables doivent exister dans l'environnement Spring, vous pouvez appeler la méthode setRequiredProperties pour transmettre le nom de la variable, afin que la méthode validateRequiredProperties soit vérifiée. Personnalisez une classe Context vous-même, assurez-vous d'appeler la méthode setRequiredProperties avant que la méthode validateRequiredProperties soit appelée pour transmettre le nom de la variable (par exemple, réécrire initPropertySources), vous pouvez laisser le printemps terminer l'inspection pour nous;

getFreshBeanFactory ()

Ensuite, regardez ConfigurableListableBeanFactory beanFactory = obtenezFreshBeanFactory (); pour obtenir la variable temporaire beanFactory, regardez d'abord la relation entre ConfigurableListableBeanFactory et BeanFactory:

Jetez un autre coup d'œil à la méthode getFreshBeanFactory:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	//由子类创建beanFactory
	refreshBeanFactory();
	//取得子类创建好的beanFactory,作为obtainFreshBeanFactory方法的返回值返回
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (logger.isDebugEnabled()) {
		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
	}
	return beanFactory;
}

RefreshBeanFactory dans certains des codes ci-dessus nécessite un examen plus approfondi;

Méthode refreshBeanFactory

La méthode refreshBeanFactory est une méthode abstraite de la classe AbstractApplicationContext. L'implémentation spécifique est dans une sous-classe. Prenant sa sous-classe AbstractRefreshableApplicationContext comme exemple, examinons l'implémentation de la méthode refreshBeanFactory:

@Override
protected final void refreshBeanFactory() throws BeansException {
	//如果beanFactory已经存在,就销毁context管理的所有bean,并关闭beanFactory
	if (hasBeanFactory()) {
		//其实就是调用一些集合的clear方法,解除对一些实例的引用,参考DefaultSingletonBeanRegistry.destroySingletons方法
		destroyBeans();
		//关闭当前的beanFactory,其实就是将成员变量beanFactory设置为null
		closeBeanFactory();
	}
	try {
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}
  1. La méthode createBeanFactory renvoie en fait une instance de DefaultListableBeanFactory:

protected DefaultListableBeanFactory createBeanFactory() {
	return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
  1. La prochaine méthode personnaliserBeanFactory est réservée à la sous-classe OverWrite. La description et le code source de la méthode sont les suivants. La description recommande le réglage spécial du beanFactory existant par OverWrite:

/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
	if (this.allowBeanDefinitionOverriding != null) {
		//allowBeanDefinitionOverriding表示是否允许注册一个同名的类来覆盖原有类(注意是类,不是实例)
		beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.allowCircularReferences != null) {
		//allowCircularReferences表示是否运行多个类之间的循环引用
		beanFactory.setAllowCircularReferences(this.allowCircularReferences);
	}
}
  1. loadBeanDefinitions est une méthode abstraite de la classe AbstractRefreshableApplicationContext, réservée à l'implémentation de la sous-classe. La fonction consiste à enregistrer toutes les définitions de bean dans le contexte. Prenez AbstractXmlApplicationContext comme exemple pour voir ce que fait la méthode loadBeanDefinitions:

/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);


	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));


	// Allow a subclass to provide custom initialization of the reader,
	// then proceed with actually loading the bean definitions.
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}

Le code ci-dessus montre que la définition du chargement des beans se fait via XmlBeanDefinitionReader, en se concentrant sur la méthode loadBeanDefinitions:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}

Dans le code ci-dessus, lequel de getConfigResources () et getConfigLocations () renverra des données valides? Cela va examiner la méthode de construction de ClassPathXmlApplicationContext:

//这个方法设置的是configLocations 
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {


	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}


//这个方法设置的是这个方法设置的是configResources 
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
			throws BeansException {


	super(parent);
	Assert.notNull(paths, "Path array must not be null");
	Assert.notNull(clazz, "Class argument must not be null");
	this.configResources = new Resource[paths.length];
	for (int i = 0; i < paths.length; i++) {
		this.configResources[i] = new ClassPathResource(paths[i], clazz);
	}
	refresh();
}

Par conséquent, qu'il s'agisse de configLocations ou configResources dépend du constructeur que nous utilisons pour instancier l'objet applicationContext;

  1. Si la façon dont nous instancions l'objet applicationContext est un nouveau ClassPathXmlApplicationContext ("applicationContext.xml") , alors la méthode setConfigLocations sera appelée, donc à l'intérieur de la méthode loadBeanDefinitions, le code réel exécuté est le suivant:

String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
  1. Vous pouvez maintenant regarder la méthode loadBeanDefinitions (String ... locations) de la classe AbstractBeanDefinitionReader:

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

Développez la méthode appelée dans la boucle for ci-dessus:

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}


		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}

Dans la méthode ci-dessus, souvenez-vous d'abord que le resourceLoader est ClassPathXmlApplicationContext (ligne de code beanDefinitionReader.setResourceLoader (cette)), toute la ligne de code resourceLoader.getResource (location) appellera éventuellement la méthode getResources (String locationPattern) de l'objet PathMatchingResourcePatternResourcePatternResource Après avoir obtenu l'objet Resource, il appellera alors la méthode loadBeanDefinitions (Resource ... resources) pour charger la définition du bean, et enfin appellera la méthode XmlBeanDefinitionReader.loadBeanDefinitions (EncodedResource encodedResource):

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}


		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

Le code ci-dessus montre qu'il est important de récupérer InputStream via l'objet Resource, puis d'appeler la méthode doLoadBeanDefinitions:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			Document doc = doLoadDocument(inputSource, resource);
			return registerBeanDefinitions(doc, resource);
		}
		...

Ce qui précède est le code clé pour charger la définition du bean: créez d'abord l'objet Document, puis appelez la méthode registerBeanDefinitions, puis placez la définition de chaque bean dans le beanDefinitionMap de DefaultListableBeanFactory, la pile détaillée est la suivante:

Après avoir terminé l'enregistrement de la définition du bean, vous pouvez revenir à la méthode AbstractRefreshableApplicationContext.refreshBeanFactory et consulter le code après loadBeanDefinitions (beanFactory):

synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}

À ce stade, la méthode refreshBeanFactory est analysée, ce que fait la méthode: Une fois la définition du bean dans le fichier xml analysée, elle est stockée dans le beanDefinitionMap de DefaultListableBeanFactory;

Maintenant, revenons à la méthode AbstractApplicationContext.refresh () de la ligne principale, nous avons analysé la méthode getFreshBeanFactory (), et toutes les définitions de bean sont stockées dans l'instance correspondant à la variable temporaire beanBean;

prepareBeanFactory

Ensuite, préparezBeanFactory (beanFactory) , jetez un œil au code source de cette méthode:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//设置类加载器
	beanFactory.setBeanClassLoader(getClassLoader());
	//设置解析器,用于解析bean的定义中出现的Spel表达式表达式
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	//设置一个注册接口,该接口只有一个方法registerCustomEditors,用来设置自定义的转换器
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));


	// 部署一个bean的后置处理器ApplicationContextAwareProcessor,用于将spring的环境信息注入到实例化的bean之中
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
	//bean在初始化的时候,如果有属性的类型为ResourceLoaderAware,则该属性不会被依赖注入
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);


	// BeanFactory interface not registered as resolvable type in a plain factory.
	// MessageSource registered (and found for autowiring) as a bean.
	//bean如果有个属性的类型为BeanFactory.class,那么该属性会被设置为beanFactory
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);


	// Detect a LoadTimeWeaver and prepare for weaving, if found.
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		// 部署一个bean的后置处理器ApplicationContextAwareProcessor,用于AOP静态代理相关的处理
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}


	// Register default environment beans.
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		//注册一个bean
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}

Les points suivants doivent être notés dans le code ci-dessus:

  1. beanFactory.addPropertyEditorRegistrar (new ResourceEditorRegistrar (this, getEnvironment ())), cette méthode doit être utilisée avec la méthode AbstractBeanFactory.registerCustomEditors pour mieux comprendre: la méthode addPropertyEditorRegistrar place un registre dans la propriété propertyEditorRegistrars, puis utilise les registres Accédez au bureau d'enregistrement dans propertyEditorRegistrars et appelez la méthode registerCustomEditors de ces bureaux d'enregistrement pour terminer le paramétrage du convertisseur personnalisé;

  2. La méthode beanFactory.addBeanPostProcessor est utilisée pour injecter le postprocesseur. Une fois l'instance de bean créée, avant et après l'exécution de la méthode d'initialisation, les méthodes postProcessBeforeInitialization et postProcessAfterInitialization du postprocesseur seront appelées séparément;

  3. beanFactory.ignoreDependencyInterface définit l'interface à ignorer lors de l'injection de dépendances. Par exemple, le bean a un type de propriété ResourceLoaderAware, cette propriété ne sera donc pas injectée dans une instance du type ResourceLoaderAware;

  4. beanFactory.registerResolvableDependency (BeanFactory.class, beanFactory) est un paramètre spécial. Si un bean possède un attribut de type BeanFactory, l'attribut sera défini sur l'instance de beanFactory;

En général, la méthode prepareBeanFactory consiste à effectuer certains travaux de configuration pour le beanFactory, à transmettre certains paramètres et classes d'outils qui seront utilisés ultérieurement, puis à créer des beans dans le conteneur de ressort;

postProcessBeanFactory

La méthode postProcessBeanFactory est réservée aux extensions de sous-classe. Vous pouvez enregistrer un post-processeur avant l'initialisation de l'instance de bean (similaire à beanFactory.addBeanPostProcessor dans la méthode prepareBeanFactory). Prenez la sous-classe AbstractRefreshableWebApplicationContext comme exemple. La méthode postProcessBeanFactory est la suivante:

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		beanFactory.ignoreDependencyInterface(ServletConfigAware.class);


		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
	}

On peut voir qu'à l'exception du travail de la classe WebApplicationContextUtils, le reste est similaire à la méthode preparationBeanFactory;

invokeBeanFactoryPostProcessors

La méthode invokeBeanFactoryPostProcessors est utilisée pour exécuter la méthode postProcessBeanFactory du postprocesseur BeanFactoryPostProcessor. En plus du postprocesseur natif, nous pouvons également l'étendre nous-mêmes pour apporter quelques modifications à la définition du bean, car le bean n'a pas encore été instancié à ce moment. , N'appelez donc pas les méthodes qui déclencheront l'instanciation du bean (comme la méthode getBeanNamesForType de BeanFactory) dans le BeanFactoryPostProcessor que vous développez. Veuillez traiter l'instance de bean dans BeanPostProcessor;

registerBeanPostProcessors

La méthode registerBeanPostProcessors a un peu plus de code, donc elle n'est pas publiée ici. Autrement dit, il s'agit de trouver tous les post-processeurs du bean (notez qu'il s'agit du post-processeur du bean, pas du post-processeur de la beanFactory. Le post-processeur traite des instances de bean, et le post-processeur beanfactory traite des définitions de bean), puis les post-processeurs de ces beans sont divisés en trois catégories:

  1. Si l'interface ordonnée Ordered.class est implémentée, elle est d'abord placée dans la collection orderPostProcessors, puis ajoutée à la collection de post-traitement du bean de la beanFactory après le tri;

  2. Il n'implémente ni le postprocesseur Ordered.class ni PriorityOrdered.class, mais rejoint également la collection de post-traitement de bean factory;

  3. Enfin, l'interface de priorité PriorityOrdered.class est implémentée. Après le tri, l'ordre est ajouté à la collection de post-traitement du beanFactory;

Après l'exécution de la méthode registerBeanPostProcessors, le post-processeur de bean ordonné a été enregistré dans beanFactory. Une fois le bean instancié, ces post-processeurs seront utilisés à leur tour pour effectuer le traitement correspondant sur l'instance de bean;

initMessageSource

La méthode initMessageSource est utilisée pour préparer les ressources internationalisées. Stockez le bean qui implémente l'interface MessageSource dans la variable membre de ApplicationContext. Vérifiez d'abord s'il existe une configuration. S'il y en a une, instanciez-la, sinon créez un bean d'instance DelegatingMessageSource;

initApplicationEventMulticaster

Au printemps, il existe des événements, des diffuseurs d'événements, des écouteurs d'événements et d'autres systèmes d'événements. Dans la méthode initApplicationEventMulticaster, le diffuseur d'événements est initialisé. Si la configuration de ce bean est introuvable, une instance SimpleApplicationEventMulticaster est créée en tant que bean diffuseur d'événements, et Enregistrer en tant que variable membre applicationEventMulticaster of applicationContext;

onRefresh

onRefresh est une méthode vide, qui est laissée à la sous-classe à implémenter. Avant que le bean ne soit instancié, effectuez quelques opérations liées à ApplicationContext. Prenons la sous-classe AbstractRefreshableWebApplicationContext comme exemple, jetez un œil à sa méthode onRefresh:

@Override
protected void onRefresh() {
	this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}

On peut voir que l'initialisation liée au thème est effectuée et enregistrée dans les variables membres d'ApplicationContext;

inscrivez-vous

La méthode est nommée registerListeners. Il semble que le nom enregistre l'écouteur dans le diffuseur d'événements, mais ce n'est pas le cas. Seuls certains écouteurs spéciaux sont enregistrés. Les classes qui implémentent l'interface ApplicationListener dans le fichier de configuration du bean n'ont aucune instance , Voici donc juste pour enregistrer son nom dans le diffuseur, l'opération d'enregistrement de ces écouteurs dans le diffuseur est terminée dans le post-processeur du bean, puis le bean a été instancié, nous regardons le code:

protected void registerListeners() {
	// 注册的都是特殊的事件监听器,而并非配置中的bean
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}


	// Do not initialize FactoryBeans here: We need to leave all regular beans
	// uninitialized to let post-processors apply to them!
	// 根据接口类型找出所有监听器的名称
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		// 这里只是把监听器的名称保存在广播器中,并没有将这些监听器实例化!!!
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}
}

finishBeanFactoryInitialization

La méthode finishBeanFactoryInitialization fait deux choses:

  1. initialisation des objets beanFactory;

  2. Les beans singleton que nous avons configurés dans le fichier de configuration du bean sont tous instanciés dans la méthode finishBeanFactoryInitialization;

Regardez le code:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// Initialize conversion service for this context.
	// 实例化类型转换的bean,并保存在ApplicationContext中
	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
		beanFactory.setConversionService(
		beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
	}


	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
	// 实例化LoadTimeWeaverAware接口的bean,用于ApsectJ的类加载期织入的处理
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}


	// Stop using the temporary ClassLoader for type matching.
	// 确保临时的classLoader为空,临时classLoader一般被用来做类型匹配的
	beanFactory.setTempClassLoader(null);


	// Allow for caching all bean definition metadata, not expecting further changes.
	// 将一个标志设置为true,表示applicationContext已经缓存了所有bean的定义,这些bean的name都被保存在applicationContext的frozenBeanDefinitionNames成员变量中,相当于一个快照,记录了当前那些bean的定义已经拿到了
	beanFactory.freezeConfiguration();


	// 实例化所有还未实例化的单例bean
	beanFactory.preInstantiateSingletons();
}

Dans le code ci-dessus, beanFactory.preInstantiateSingletons () doit être développé avec soin:

public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}


		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);


		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			// 获取bean的定义,该定义已经和父类定义做了合并
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 非抽象类、是单例、非懒加载
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				//FactoryBean的处理
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
					//非FactoryBean的实例化、初始化
					getBean(beanName);
				}
			}
		}


		// Trigger post-initialization callback for all applicable beans...
		// 单例实例化完成后,如果实现了SmartInitializingSingleton接口,afterSingletonsInstantiated就会被调用,此处用到了特权控制逻辑AccessController.doPrivileged
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
			}
		}
	}
}

Dans le code ci-dessus, nous devons nous concentrer sur getBean (beanName), qui instanciera le bean. Parce qu'il y a trop de contenu à développer dans ce chapitre, nous trions d'abord le chemin d'appel du bean instancié:

AbstractBeanFactory.getBean(String name)


->


AbstractBeanFactory.doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly)


->


DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)


->


AbstractBeanFactory.doGetBean中的匿名类的getObject方法


->


AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, Object[] args) 


->
AbstractAutowireCapableBeanFactory.doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args)


->


AbstractAutowireCapableBeanFactory.createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)


->


instantiateBean(final String beanName, final RootBeanDefinition mbd)


->


SimpleInstantiationStrategy.instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner)


->


BeanUtils.instantiateClass(Constructor<T> ctor, Object... args)


->


Constructor.newInstance(Object ... initargs)


->


bean的构造方法

Il peut être vu à partir du chemin d'appel ci-dessus que la création de l'objet bean est créée par réflexion dans la méthode BeanUtils.instantiateClass;

Voyons quand les variables membres du bean sont injectées avec des valeurs. Comme le montre la figure ci-dessous, dans la méthode AbstractAutowireCapableBeanFactory.doCreateBean, appelez d'abord createBeanInstance pour créer l'objet bean (affiché dans la zone verte), puis appelez la méthode populateBean pour injecter du contenu dans la variable membre (zone rouge Montré):

Écrivez une description de l'image ici

La pile d'appels de la valeur injectée est organisée comme suit: on voit que l'injection se fait également par réflexion:

AbstractAutowireCapableBeanFactory.populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)


->


AbstractAutowireCapableBeanFactory.applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)


->


AbstractPropertyAccessor.setPropertyValues(PropertyValues pvs)


->


BeanWrapperImpl.setPropertyValue(PropertyValue pv)


->


Method.invoke(Object obj, Object... args)

Après avoir lu la logique d'injection des variables membres, il existe une autre logique importante. Faites attention à l'initialisation du bean (l'attribut init-method dans le fichier de configuration du bean). Dans la méthode AbstractAutowireCapableBeanFactory.doCreateBean, appelez la méthode populateBean à la variable membre Immédiatement après l'injection de la valeur, la méthode initializeBean est appelée pour l'initialisation et la pile d'appels est organisée comme suit:

AbstractAutowireCapableBeanFactory.initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)


->


AbstractAutowireCapableBeanFactory.invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)


->


AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)


->


Method.invoke(Object obj, Object... args)

On peut voir que la méthode d'initialisation est toujours effectuée par réflexion;

finishRefresh

La dernière méthode est finishRefresh, qui consiste en certaines opérations après l'instanciation et l'initialisation du bean, telles que les rappels pour les changements de cycle de vie, l'envoi de diffusions d'achèvement de l'actualisation applicationContext, etc., développez pour voir:

protected void finishRefresh() {
	// 检查是否已经配置了生命周期处理器,如果没有就new一个DefaultLifecycleProcessor
	initLifecycleProcessor();


	// 找到所有实现了Lifecycle接口的bean,按照每个bean设置的生命周期阶段进行分组,再依次调用每个分组中每个bean的start方法,完成生命周期监听的通知
	getLifecycleProcessor().onRefresh();


	// 创建一条代表applicationContext刷新完成的事件,交给广播器去广播
	publishEvent(new ContextRefreshedEvent(this));


	// 如果配置了MBeanServer,就完成在MBeanServer上的注册
	LiveBeansView.registerApplicationContext(this);
}

Jusqu'à présent, nous avons réussi l'ensemble du processus d'initialisation, mais l'espace est limité et de nombreux détails n'ont pas été étendus. En outre, de nombreuses sous-classes ont également leurs propres extensions uniques. Toutes doivent prendre du temps à regarder attentivement. J'espère que cet article peut vous aider à organiser vos idées à partir de Comprendre les étapes clés de l'initialisation en général, afin de ne pas entrer dans les détails prématurément;

发布了376 篇原创文章 · 获赞 986 · 访问量 128万+

Je suppose que tu aimes

Origine blog.csdn.net/boling_cavalry/article/details/105463314
conseillé
Classement