正常来说,对于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 MapbeanDefinitionMap = 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); ListupdatedDefinitions = 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技术内幕>>