4、spring初始化过程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4、spring初始化过程相关的知识,希望对你有一定的参考价值。
参考技术A spring初始化过程也叫ioc容器初始化过程、bean生命周期。1、从main函数开始
2、进入new ClassPathXmlApplicationContex()构造方法,看ioc容器的创建过程
3、进入AbstractRefreshableConfigApplicationContext类的this.setConfigLocations()方法,可以看到参数locations的内容如下图
把参数location解析完路径后放入string[] configLocations中,如下图
返回上一层ClassPathXmlApplicationContext构造方法
4、进入AbstractApplicationContext类的this.refresh()方法,该方法是重点
4.1、进入this.prepareRefresh()方法
看看this.getEnvironment()内容,jdk环境信息和转换器
继续看validateRequiredProperties()方法
继续进入AbstractPropertyResolver.validateRequiredProperties()
至此,this.prepareRefresh()方法解析完毕,总结下它的作用:
4.2、进入this.obtainFreshBeanFactory()方法
方法this.refreshBeanFactory()是重点,进入
创建DefaultListableBeanFactory对象,可以理解为就是ApplicationContext
方法this.loadBeanDefinitions(beanFactory)是重点,进入
创建XmlBeanDefinitionReader对象,用于解析xml文件中定义的bean
方法this.loadBeanDefinitions(beanDefinitionReader)是重点,进入
接下来是bean解析和保存的过程,一路进入到AbstractBeanDefinitionReader.loadBeanDefinitions(String location, Set<Resource> actualResources)方法
拿到resource[xml文件],进入this.loadBeanDefinitions(resources)方法,一路往下走,最终又会进入XmlBeanDefinitionReader.(EncodedResource encodedResource)方法
进入this.doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,把resource解析为doc
进入this.registerBeanDefinitions(doc, resource)方法
进入documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource))方法
进入this.doRegisterBeanDefinitions(root)方法
进入this.parseBeanDefinitions(root, this.delegate)方法
进入this.parseDefaultElement(ele, delegate)方法
进入this.processBeanDefinition(ele, delegate)方法,将xml中的bean信息解析封装为BeanDefinition对象
进入BeanDefinitionReaderUtils.registerBeanDefinition()方法,将BeanDefinition对象注册到BeanFactory中
至此this.obtainFreshBeanFactory()方法方法解析完毕,总结它的作用:
4.3、进入this.prepareBeanFactory(beanFactory)方法
该方法作用,主要给beanFactory设置相关属性:
1、类加载器、类表达式解析器、属性编辑注册器
2、添加bean后处理器等
3、资源注册
4、注册默认的environment beans
4.4、this.postProcessBeanFactory(beanFactory)不用关注。
4.5、进入this.invokeBeanFactoryPostProcessors(beanFactory)方法
调用实现PriorityOrdered的BeanDefinitionRegistryPostProcessors
调用实现Ordered的BeanDefinitionRegistryPostProcessors
总结该方法作用:
4.6、进入this.registerBeanPostProcessors(beanFactory)方法
BeanPostProcessor与BeanFactoryPostProcessor很类似,也是存在优先级的,但BeanPostProcessor在处理完之后,并没有立即调用processor的方法,而是将一系列的BeanPostProcessor实体对象注册到BeanFactory中。在后面的初始化过程中会使用到这些Processor。
总结该方法作用:
4.7、 方法initMessageSource()是空方法,不用关注
4.8、进入initApplicationEventMulticaster()方法
总共该方法作用:
4.9、方法this.onRefresh()是空方法,不用关注
4.10、进入this.registerListeners()方法
总结该方法作用:
4.11、进入this.finishBeanFactoryInitialization(beanFactory)方法,该方法也是重点
该方法调用过程:【数据来源顶部链接】
这个方法就是实例化beanFactory中的BeanDefinition,该方法解决了bean循环引入问题,具体分析放到下次更新。
4.12、方法this.finishRefresh()暂不介绍。
5、总结spring初始化重要的8个方法:
spring初始化过程到此结束。如果错漏地方,望指出!!!
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日,下午。
相关内容
以上是关于4、spring初始化过程的主要内容,如果未能解决你的问题,请参考以下文章