博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring IoC容器初的初始化过程
阅读量:5738 次
发布时间:2019-06-18

本文共 13010 字,大约阅读时间需要 43 分钟。

hot3.png

正常来说,对于IOC容器启动,我们首先想到的是初始化何时启动?启动包括哪几个步骤呢?

先回答下结果,后面具体看下这个完整的过程.

启动是通过AbstractApplicationContext的refresh()方法进行启动的,这个方法标准IoC容器的正式启动

具体来说,这个启动包括BeanDefinition的Resouce定位、载入和注册三个基本过程。如果我们了解如何编程式地使用IoC容器,就可以清楚地看到Resource定位和载入过程的接口调用。.具体分别如下:

  • 第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDef-inition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中的Bean定义信息可以使用前面提到的ClassPathResource来使用,等等。这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。
  • 第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IoC容器能够方便地对POJO对象也就是Bean进行管理。在下面的章节中,我们会对这个载入的过程进行详细的分析,使大家对整个过程有比较清楚的了解。
  • 第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,我们可以看到,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的.

        值得注意的是,这里谈的是IoC容器初始化过程,在这个过程中,一般不包含Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程,下面我们看看这个启动过程.

在使用IoC容器时,需要如下几个步骤:

1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息.

2)创建一个BeanFactory,比如常用的DefaultListableBeanFactory。

3)创建一个载入BeanDefinition的读取器,比如XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。

4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

比如以我们常用的FileSystemXmlApplicationContext和ClassPathXmlApplicationContext两种加载context文件,看下这两者的继承层子结构:

不难看出,两者的继承层次接口一模一样,区别也不是很大,具体如下:

ClassPathXmlApplicationContext 

默认文件路径是src下那一级
classpath:和classpath*:的区别: 
classpath: 只能加载一个配置文件,如果配置了多个,则只加载第一个 
classpath*: 可以加载多个配置文件,如果有多个配置文件,就用这个

FileSystemXmlApplicationContext 

这个类,默认获取的是项目路径,默认文件路径是项目名下一级,与src同级。
如果前边加了file:则说明后边的路径就要写全路径了,就是绝对路径
file:D:/workspace/applicationContext.xml

以FileSystemXmlApplicationContext为例.在FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能已经在FileSys-temXmlApplicationContext的基类AbstractXmlApplication-Context中实现了,在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。一个功能是,如果应用直接使用FileSystemXmlApplication-Context,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程,这个方法标志着IoC容器的正式启动。这在FileSystemApplicationContext的代码实现中可以看到,代码如下:

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

refresh()是来自AbstractApplicationContext里面,ClassPathXmlApplicationContext的refresh()也是出自

AbstractApplicationContext.refresh()方法代码如下(从里面也基本可以看出初始化的大致过程):

public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        // Prepare this context for refreshing.        //准备启动spring容器,设置容器的启动日期和活动标志         prepareRefresh();        // Tell the subclass to refresh the internal bean factory.        //获得容器ApplicationContext的子类BeanFactory。步骤如下:        //1.如果已经有了BeanFactory就销毁它里面的单例Bean并关闭这个BeanFactory。        //2.创建一个新的BeanFactory。        //3.对这个BeanFactory进行定制(customize),如allowBeanDefinitionOverriding等参数        //4.转载BeanDefinitions(读取配置文件,将xml转换成对应得BeanDefinition)        //5.检查是否同时启动了两个BeanFactory。          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();        // Prepare the bean factory for use in this context.        //配置BeanFactory(就是将ApplicationContext的一些属性配置到BeanFactory上吧)     //如重要的如设置classLoader;将BeanPostProcess注册到BeanFactory里          prepareBeanFactory(beanFactory);        try {        // Allows post-processing of the bean factory in context subclasses.        //允许上下文的子类去执行postProcessor          postProcessBeanFactory(beanFactory);        // Invoke factory processors registered as beans in the context.        // 执行注册到该上下文的BeanFactoryPostProcessors        invokeBeanFactoryPostProcessors(beanFactory);        // Register bean processors that intercept bean creation.        // 开始注册BeanPostProcessor来拦截其他的bean的初始化过程        registerBeanPostProcessors(beanFactory);        // Initialize message source for this context.        // 初始化消息源        initMessageSource();        // Initialize event multicaster for this context.        //注册上下文事件的广播集          initApplicationEventMulticaster();        // Initialize other special beans in specific context subclasses.        //初始化一些特殊的bean        onRefresh();        // Check for listener beans and register them.        //查询并校验监听器并注册        registerListeners();        // Instantiate all remaining (non-lazy-init) singletons.        /// 实例化所有非懒加载的所有bean        finishBeanFactoryInitialization(beanFactory);        // Last step: publish corresponding event.        //最后一步发布所有的运用        finishRefresh();        }        catch (BeansException ex) {            if (logger.isWarnEnabled()) {                logger.warn("Exception encountered during context initialization - " +                "cancelling refresh attempt: " + ex);            }            // Destroy already created singletons to avoid dangling resources.            destroyBeans();            // Reset 'active' flag.            cancelRefresh(ex);            // Propagate exception to caller.            throw ex;        }        finally {            // Reset common introspection caches in Spring's core, since we            // might not ever need metadata for singleton beans anymore...            resetCommonCaches();        }    }}

obtainFreshBeanFactory方法如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {        //第一步对Resource定位		refreshBeanFactory();		ConfigurableListableBeanFactory beanFactory = getBeanFactory();		if (logger.isDebugEnabled()) {			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);		}		return beanFactory;	}

refreshBeanFactory方法源码如下:

protected final void refreshBeanFactory() throws BeansException {		if (hasBeanFactory()) {			destroyBeans();			closeBeanFactory();		}		try {			DefaultListableBeanFactory beanFactory = createBeanFactory();			beanFactory.setSerializationId(getId());			customizeBeanFactory(beanFactory);            //第二步BeanDefinition的载入,第三步对载入的BeanDefinition在Ioc上注册			loadBeanDefinitions(beanFactory);			synchronized (this.beanFactoryMonitor) {				this.beanFactory = beanFactory;			}		}		catch (IOException ex) {			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);		}	}

getBeanFactory方法源码如下:

@Override	public final ConfigurableListableBeanFactory getBeanFactory() {		synchronized (this.beanFactoryMonitor) {			if (this.beanFactory == null) {				throw new IllegalStateException("BeanFactory not initialized or already closed - " +						"call 'refresh' before accessing beans via the ApplicationContext");			}			return this.beanFactory;		}	}

总结意思就是:obtainFreshBeanFactory准备容器,refreshBeanFactory产生DefaultListableBeanFactoryro容器,getBeanFactory是对产生的容器校验,避免失败.可以看出其实真正使用的IoC容器是DefaultListableBeanFactory.

 

1. BeanDefinition的Resource定位

这个FileSystemXmlApplicationContext已经通过继承AbstractApplicationContext具备了Re-sourceLoader读入以Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是DefaultRe-sourceLoader。

FileSystemXmlApplicationContex通过构造得到一个 在文件系统中定位的BeanDefinition, 这个getResourceByPath是在BeanDefinitionReader的loadBeanDefintion中被调用的.交互过程如下:

2. BeanDefinition的载入

      同时发现,refreshBeanFactory里面loadBeanDefinitions(beanFactory); 就是初始化的第二步,即BeanDefinition的载入.

/**	 * Load the bean definitions with the given XmlBeanDefinitionReader.	 * 

The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory} * method; hence this method is just supposed to load and/or register bean definitions. * @param reader the XmlBeanDefinitionReader to use * @throws BeansException in case of bean registration errors * @throws IOException if the required XML document isn't found * @see #refreshBeanFactory * @see #getConfigLocations * @see #getResources * @see #getResourcePatternResolver*/ 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); } }

reader.loadBeanDefinitions方法进去到AbstractBeanDefinitionReader的loadBeanDefinitions:

@Overridepublic int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {   Assert.notNull(resources, "Resource array must not be null");   int counter = 0;   for (Resource resource : resources) {      counter += loadBeanDefinitions(resource);   }   return counter;}

 

由于调用栈比较深,就不一一贴源码了,具体的Spring BeanDefinition的解析是在BeanDefinition-ParserDelegate中完成的。BeanDefinition载入中的交互过程如下:

3. BeanDefinition在IoC容器中的注册

    在第二步对BeanDefinition载入后,但是这些数据还不能直接被Ioc容器使用,需要在IoC容器中对这些BeanDefinition数据进行注册。这个注册为IoC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中可以看到,如下所示。

/** Map of bean definition objects, keyed by bean name */private final Map
beanDefinitionMap = new ConcurrentHashMap<>(256);

将解析得到的BeanDefinition向IoC容器中的beanDefini-tionMap注册的过程是在载入BeanDefinition完成后进行的.注册交互过程如下:

    

DefaultListableBeanFactory容器的registerBeanDefinition方法如下:

//---------------------------------------------------------------------	// Implementation of BeanDefinitionRegistry interface	//---------------------------------------------------------------------	@Override	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)			throws BeanDefinitionStoreException {		Assert.hasText(beanName, "Bean name must not be empty");		Assert.notNull(beanDefinition, "BeanDefinition must not be null");		if (beanDefinition instanceof AbstractBeanDefinition) {			try {				((AbstractBeanDefinition) beanDefinition).validate();			}			catch (BeanDefinitionValidationException ex) {				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,						"Validation of bean definition failed", ex);			}		}		BeanDefinition oldBeanDefinition;		oldBeanDefinition = this.beanDefinitionMap.get(beanName);		if (oldBeanDefinition != null) {			if (!isAllowBeanDefinitionOverriding()) {				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +						"': There is already [" + oldBeanDefinition + "] bound.");			}			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE				if (this.logger.isWarnEnabled()) {					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +							"' with a framework-generated bean definition: replacing [" +							oldBeanDefinition + "] with [" + beanDefinition + "]");				}			}			else if (!beanDefinition.equals(oldBeanDefinition)) {				if (this.logger.isInfoEnabled()) {					this.logger.info("Overriding bean definition for bean '" + beanName +							"' with a different definition: replacing [" + oldBeanDefinition +							"] with [" + beanDefinition + "]");				}			}			else {				if (this.logger.isDebugEnabled()) {					this.logger.debug("Overriding bean definition for bean '" + beanName +							"' with an equivalent definition: replacing [" + oldBeanDefinition +							"] with [" + beanDefinition + "]");				}			}			this.beanDefinitionMap.put(beanName, beanDefinition);		}		else {			if (hasBeanCreationStarted()) {				// Cannot modify startup-time collection elements anymore (for stable iteration)				synchronized (this.beanDefinitionMap) {					this.beanDefinitionMap.put(beanName, beanDefinition);					List
updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set
updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }

        通过跟踪以上的代码调用去看一下具体的注册实现,在De-faultListableBeanFactory中实现了BeanDefinitionRegistry的接口,这个接口的实现完成BeanDefinition向容器的注册。这个注册过程不复杂,就是把解析得到的BeanDefinition设置到hashMap中去。需要注意的是,如果遇到同名的BeanDefinition,进行处理的时候需要依据allowBeanDefinitionOverriding的配置来完成。

        以上就是Ioc容器初始化的3个过程,进行启动的方法入口是refresh(),这个方法标准IoC容器的正式启动.

 

参考:

1.https://github.com/spring-projects/spring-framework.

2.<<Spring技术内幕>>

转载于:https://my.oschina.net/mindfind/blog/918515

你可能感兴趣的文章
Visifire charts ToolBar
查看>>
Mysql查询
查看>>
数据传输流程和socket简单操作
查看>>
ProbS CF matlab源代码(二分系统)(原创作品,转载注明出处,谢谢!)
查看>>
OC中KVC的注意点
查看>>
JQ入门(至回调函数)
查看>>
【洛天依】几首歌的翻唱(无伴奏)
查看>>
OpenSSL初瞻及本系列的博文的缘由
查看>>
ISO8583接口的详细资料
查看>>
tmux不自动加载配置文件.tmux.conf
查看>>
经验分享:JavaScript小技巧
查看>>
[MOSEK] Stupid things when using mosek
查看>>
程序实例---栈的顺序实现和链式实现
查看>>
服务的使用
查看>>
Oracle 用户与模式
查看>>
MairDB 初始数据库与表 (二)
查看>>
拥在怀里
查看>>
chm文件打开,有目录无内容
查看>>
whereis、find、which、locate的区别
查看>>
一点不懂到小白的linux系统运维经历分享
查看>>