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日,下午。


相关内容

Spring IOC的初始化过程——基于XML配置(二)





















以上是关于4、spring初始化过程的主要内容,如果未能解决你的问题,请参考以下文章

Spring的Bean初始化过程和生命周期

javaEE项目启动过程中Spring是怎么一步步执行的?

Spring 依赖注入

Spring IOC的初始化过程——基于XML配置

Spring初始化过程到AOP

spring internalTransactionAdvisor 事务 advisor 初始化过程