Spring IOC的初始化过程——基于XML配置
Posted morewindows0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IOC的初始化过程——基于XML配置相关的知识,希望对你有一定的参考价值。
前言:在面试过程中,Spring IOC的初始化过程,基本上属于必答题,笔者的亲身经历。因此本文基于Spring的源码对其IOC的初始化过程进行总结。
注:spring版本为4.3.0。
1.调试前准备
在spring源码中,添加如下内容(有关spring源码如何导入idea,请查询相关资料):
说明:
①User为简单bean,含有name和gender两个属性。
②User.xml为spring配置文件,仅仅对User进行简单的配置。
③SpringIoCDebug为测试类。
先看执行结果:
执行成功,说明spring源码导入及相关配置成功。
2.调试过程
Spring IOC的初始化主线为读取xml文件——>解析xml文件——>注册到BeanFactory中,但是其整个脉络十分庞大,调试起来十分的繁琐。
#1.debug进入ClassPathXmlApplicationContext类的构造函数
①super中parent为null,主要对AbstractApplicationContext中相关变量进行设置。
②setConfigLocations对xml文件资源进行设置。
③核心函数refresh(),实现了IOC的初始化。
#2.refresh源码(注refresh为AbstractApplicationContext中的函数,并且该函数为同步函数(synchronized代码块))
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 prepareRefresh(); 5 6 // Tell the subclass to refresh the internal bean factory. 7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 try { 13 // Allows post-processing of the bean factory in context subclasses. 14 postProcessBeanFactory(beanFactory); 15 16 // Invoke factory processors registered as beans in the context. 17 invokeBeanFactoryPostProcessors(beanFactory); 18 19 // Register bean processors that intercept bean creation. 20 registerBeanPostProcessors(beanFactory); 21 22 // Initialize message source for this context. 23 initMessageSource(); 24 25 // Initialize event multicaster for this context. 26 initApplicationEventMulticaster(); 27 28 // Initialize other special beans in specific context subclasses. 29 onRefresh(); 30 31 // Check for listener beans and register them. 32 registerListeners(); 33 34 // Instantiate all remaining (non-lazy-init) singletons. 35 finishBeanFactoryInitialization(beanFactory); 36 37 // Last step: publish corresponding event. 38 finishRefresh(); 39 } 40 41 catch (BeansException ex) { 42 if (logger.isWarnEnabled()) { 43 logger.warn("Exception encountered during context initialization - " + 44 "cancelling refresh attempt: " + ex); 45 } 46 47 // Destroy already created singletons to avoid dangling resources. 48 destroyBeans(); 49 50 // Reset ‘active‘ flag. 51 cancelRefresh(ex); 52 53 // Propagate exception to caller. 54 throw ex; 55 } 56 57 finally { 58 // Reset common introspection caches in Spring‘s core, since we 59 // might not ever need metadata for singleton beans anymore... 60 resetCommonCaches(); 61 } 62 } 63 }
①prepareRefresh()函数中主要设置开始时间和活动标志,this.active.set(true)。
②核心代码:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(),BeanFactory的创建和Bean的解析都在该函数中进行。具体调试如下:
进入refreshBeanFactory函数中可以发现,该方法属于AbstractRefreshableApplicationContext。首先会判断是否存在BeanFactory,如果存在则销毁当前Bean及BeanFactory。
createBeanFactory()方法,创建BeanFactory,主要关注DefaultListableBeanFactory中的beanDefinitionMap成员变量,该变量用于后续存储bean相关数据。
接着设置beanFactory的序列号,是否需要定制化beanFactory。
关键函数loadBeanDefinitions,该函数的参数为刚创建的beanFactory,进入该函数。
首先根据当前的beanFactory创建XmlBeanDefinitionReader,接着对beanDefinitionReader,进行一些相关设置,如环境、资源导入器和实体解析器。我们进入关键的loadBeanDefinitions函数。
由于我们使用的是配置文件位置类型,直接转入reader.loadBeanDefinitions中。
loadBeanDefinitions函数返回类型为int,表示实际装载bean的个数。继续进入loadBeanDefinitions函数(在AbstractBeanDefinitionReader中对此函数进行了重载)。
继续往下走:
这里获取User.xml配置文件,并转换为Resource类型。继续往下走,又发现了loadBeanDefinitions函数,进入函数后会走到如下图所示的函数中。
在XmlBeanDefinitionReader的loadBeanDefinitions中,进入核心函数
从上图中可以很清晰的看出,该函数的主要功能创建一个Document,并进行Bean的注册。在doLoadDocument函数中,对输入的资源进行了相应解析。
接下来转入registerBeanDefinitions函数。
首先会创建一个documentReader对象,利用该对象对doc对象进行注册。可以看到countBefore的值为0,表示目前在beanFactory中没有注册过bean。转入registerBeanDefinitions函数,会调用DefaultBeanDefinitionDocumentReader中。
在doc中获取得到root。
转入doRegisterBeanDefinitions中。
通过委派的方式,首先设置bean的一些默认属性,如是否懒加载等。解析Bean的主要流程如下。
通过循环找到bean标签后,利用parseDefaultElement函数进行解析。
该函数会根据标签的不同类型进行对应解析,这里我们解析bean标签,转入该函数。
这里是通过BeanDefinitionParserDelegate委派的形式来进行bean的解析。函数源码如下
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { 2 String id = ele.getAttribute(ID_ATTRIBUTE); //获得bean标签中id 3 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//获取bean标签中name属性,如果存在 4 5 List<String> aliases = new ArrayList<>(); 6 if (StringUtils.hasLength(nameAttr)) { 7 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 8 aliases.addAll(Arrays.asList(nameArr)); 9 } 10 //bean别名检查 11 String beanName = id; 12 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 13 beanName = aliases.remove(0); 14 if (logger.isDebugEnabled()) { 15 logger.debug("No XML ‘id‘ specified - using ‘" + beanName + 16 "‘ as bean name and " + aliases + " as aliases"); 17 } 18 } 19 //检查beanName是否唯一 20 if (containingBean == null) { 21 checkNameUniqueness(beanName, aliases, ele); 22 } 23 //该函数中会对bean元素的相关属性进行解析。如id和class 24 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 25 if (beanDefinition != null) { 26 if (!StringUtils.hasText(beanName)) { 27 try { 28 if (containingBean != null) { 29 beanName = BeanDefinitionReaderUtils.generateBeanName( 30 beanDefinition, this.readerContext.getRegistry(), true); 31 } 32 else { 33 beanName = this.readerContext.generateBeanName(beanDefinition); 34 // Register an alias for the plain bean class name, if still possible, 35 // if the generator returned the class name plus a suffix. 36 // This is expected for Spring 1.2/2.0 backwards compatibility. 37 String beanClassName = beanDefinition.getBeanClassName(); 38 if (beanClassName != null && 39 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 40 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 41 aliases.add(beanClassName); 42 } 43 } 44 if (logger.isDebugEnabled()) { 45 logger.debug("Neither XML ‘id‘ nor ‘name‘ specified - " + 46 "using generated bean name [" + beanName + "]"); 47 } 48 } 49 catch (Exception ex) { 50 error(ex.getMessage(), ele); 51 return null; 52 } 53 } 54 String[] aliasesArray = StringUtils.toStringArray(aliases); 55 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 56 } 57 58 return null; 59 }
通过bean标签的解析创建AbstractBeanDefinition对象。
最后生成BeanDefinitionHolder对象:bdHolder。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);这里将类的属性值加载到bdHolder中。
最终注册实例,转入其函数内部,可以看到如下操作。
到这里在最初的refresh函数中的obtainFreshBeanFactory方法基本上就算走完了,调试起来非常的繁杂。该方法中,既生成了beanFactory,又解析了xml文件,并将xml文件中的bean标签中的相关信息注册到beanFactory中。
后续流程如下:
1 //配置beanFactory,如环境、加载器等
prepareBeanFactory(beanFactory); 2 3 try { 4 // Allows post-processing of the bean factory in context subclasses.
//该函数体为空
5 postProcessBeanFactory(beanFactory); 6 7 // Invoke factory processors registered as beans in the context.
//调用工厂处理器。 8 invokeBeanFactoryPostProcessors(beanFactory); 9 10 // Register bean processors that intercept bean creation.
//注册bean的PostProcessor 11 registerBeanPostProcessors(beanFactory); 12 13 // Initialize message source for this context.
//初始化信息源,主要和国际化相关 14 initMessageSource(); 15 16 // Initialize event multicaster for this context.
//初始化事件传播器 17 initApplicationEventMulticaster(); 18 19 // Initialize other special beans in specific context subclasses.
//函数体为空 20 onRefresh(); 21 22 // Check for listener beans and register them.
//注册监听器 23 registerListeners(); 24 25 // Instantiate all remaining (non-lazy-init) singletons.
//完成bean的初始化,该函数中就会创建bean,通过反射,进行bean的创建。 26 finishBeanFactoryInitialization(beanFactory); 27 28 // Last step: publish corresponding event.
//最后完成整个ioc初始化,发布事件。 29 finishRefresh(); 30 }
至此,Spring IOC的基本流程走完,限于篇幅原因,将在下篇随笔中对IOC的初始化过程进行总结,请参看:Spring IOC的初始化过程——基于XML配置(二)
by Shawn Chen,2018.6.10日,下午。
相关内容
以上是关于Spring IOC的初始化过程——基于XML配置的主要内容,如果未能解决你的问题,请参考以下文章