Spring IOC源码分析(一)

ps:整个系列之前使用有道云整理的,所以代码都是截图格式

1.相关类介绍

1.1 Resource类

Resource类是Spring对资源的抽象。它的每一个实现类都代表了一种资源的访问策略,如 ClassPathResource、RLResource、FileSystemResource 等。

 

1.2 ResourceLoader类

有了资源,就应该有资源加载,Spring 利用ResourceLoader资源加载器来进行统一资源加载,类图如下

 

1.3 PropertySource类

PropertySource是Spring对属性配置的抽象,主要用来保存容器初始化时的一些配置信息。

其中PropertySources接口继承了Iterable接口,用来完成对PropertySource的遍历。其有一个唯一的实现类MutablePropertySources

 

1.4 PropertyResolver类

PropertyResolver类是用来加载资源文件,并生成PropertySource对象的解析器。它可以从当前环境,配置文件或者Web环境下获取多种配置信息

 

1.5 BeanDefinition类

BeanDefinition是Spring对Bean的抽象,封装了各种Bean应有的属性

 

1.6 BeanDefinitionReader类

BeanDefinitionReader的作用是读取 Spring 的配置文件(Resource)的内容,并将其转换成 Ioc 容器内部的数据结构,即BeanDefinition

使用注解扫描时,spring会使用ComponentScanBeanDefinitionParser去扫描注解。再使用ClassPathBeanDefinitionScanner来解析资源和创建BeanDefinition,而ClassPathBeanDefinitionScanner会调用MetadataReader来获取类的信息。MetadataReader使用MetadataReaderFactory创建

 

1.7 BeanFactory类

Spring IOC最基本的数据结构,是一个非常纯粹的 bean 容器,其中 BeanDefinition 是它的基本结构。BeanFactory 内部维护着一个BeanDefinition map ,并可根据BeanDefinition 的描述进行 bean 的创建和管理。

 

1.8 ApplicationContext类

这个就是大名鼎鼎的 Spring 容器,它叫做应用上下文,与我们应用息息相关。它继承 BeanFactory ,所以它是 BeanFactory 的扩展升级版,如果BeanFactory 是屌丝的话,那么 ApplicationContext 则是名副其实的高富帅。由于 ApplicationContext 的结构就决定了它与 BeanFactory 的不同,其主要区别有:

  •  org.springframework.context.MessageSource 接口,提供国际化的标准访问策略。
  •  org.springframework.context.ApplicationEventPublisher 接口,提供强大的事件机制。
  • ResourceLoader ,可以用来加载多种 Resource ,可以灵活访问不同的资源。
  • Web 应用的支持

 

2.Spring容器初始化流程

本章将简要描述Spring容器的初始化流程,注意,代码中父子类之间的调用关系以ClassPathXmlApplicationContext为例,不同的ApplicationContext调用的方法可能不一样,具体以实际情况为主

2.1 初始化上下文及其父类

初始化AbstractApplicationContext时,会设置其resourcePatternResolver字段:

getResourcePatternResolver方法返回一个PathMatchingResourcePatternResolver(继承自ResourceLoader)的资源解析器,用于后面步骤中解析资源

 

2.2 定位资源和读取配置信息

调用父类AbstractRefreshableConfigApplicationContext的setConfigLocation方法,定位资源文件的位置

其中,resolvePath方法用于解析路径,主要是解析路径中存在的占位符

调用AbstractApplicationContext的getEnvironment方法:

初始化一个标准环境类StandardEnvironment

StandardEnvironment的初始化会引起AbstractEnvironment类的初始化:

接着会调用StandardEnvironment的customizePropertySources方法,把操作系统和JVM的属性配置到属性资源中去,即AbstractEnvironment类中的MutablePropertySources类型的字段propertySources中保存。MutablePropertySources类在1.3节提到过,用于保存和遍历PropertySource,即Spring抽象的属性资源

获取ConfigurableEnvironment完毕后,调用ConfigurableEnvironment的resolveRequiredPlaceholders方法,解析占位符。实际调用的是AbstractPropertyResolver的resolveRequiredPlaceholders方法:

createPlaceholderHelper方法:

doResolvePlaceholders方法:

PropertyPlaceholderHelper的replacePlaceholders方法:

parseStringValue方法使用递归的方式解析路径中的占位符,具体代码逻辑不再赘述,感兴趣的话以后再看。

小结:设置配置路径的过程使用到了PropertySource和PropertyResolver类和其相关的子类。完成了属性资源的配置和配置路径的解析工作。经过层层调用,setConfigLocations方法完成之后,会把解析完成的路径放到AbstractRefreshableConfigApplicationContext的configLocations字段中,供后面资源加载器(ResourceLoader)进行处理

 

2.3 刷新容器

接着,会调用AbstractApplicationContext的refresh方法,进行容器的刷新操作

2.3.1 prepareRefresh方法

调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识

initPropertySources方法,初始化配置资源。这里会调用AbstractRefreshableWebApplicationContext的initPropertySources方法,初始化Servlet相关配置

 

2.3.2 obtainFreshBeanFactory方法

告诉子类启动refreshBeanFactory方法,Bean定义资源文件的载入从子类的refreshBeanFactory方法启动

这里调用的是AbstractRefreshableApplicationContext类中的refreshBeanFactory方法:

其中,createBeanFactory方法返回的是DefaultListableBeanFactory类型的BeanFactory

customizeBeanFactory主要配置两个属性,allowBeanDefinitionOverriding和allowCircularReferences:

allowBeanDefinitionOverriding字段用来表示是否允许BeanDefinition的重写,设置为true时,如果出现同名的BeanDefinition,后者会覆盖前者。如果设置为false,容器会抛出异常

allowCircularReferences资源用来表示是否允许Bean的循环依赖,如果设置为true,容器遇到循环依赖时会自动处理,否则将抛出异常

接下来,是最重要的loadBeanDefinitions方法。这里会调用AbstractXmlApplicationContext的loadBeanDefinitions方法:

先分析beanDefinitionReader的初始化过程:由上一章的类图可知,DefaultListableBeanFactory继承了BeanDefinitionRegistry类,所以可以作为XmlBeanDefinitionReader(BeanDefinitionRegistry registry)构造方法的参数:

XmlBeanDefinitionReader的初始化引起了AbstractBeanDefinitionReader的初始化:

beanDefinitionReader初始化完毕之后,会调用loadBeanDefinitons的重载方法:loadBeanDefinitions(XmlBeanDefinitionReader reader)

由于在初始化ClassPathXmlApplicationContext时,使用的是字符串配置,没有初始化Resource数组。所以,这里真正调用的是AbstractBeanDefinitionReader的loadBeanDefinitions(String... locations)方法:

最终会调用AbstractBeanDefinitionReader的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)方法:

由上面的分析可以得知,resourceLoader就是ClassPathXmlApplicationContext,是ResourcePatternResolver的子类。所以,我们开始分析getResources方法。这里的

ResourcePatternResolver接口的getResources方法由AbstractApplicationContext类实现:

这里的resourcePatternResolver是不是很熟悉?没错,它就是第一步初始化AbstractApplicationContext时设置的资源解析器,类型是PathMatchingResourcePatternResolver

所以,调用的是PathMatchingResourcePatternResolver的getResources方法:

其中,findPathMatchingResources比较重要,单独提出来说明:

路径解析完成,获取到Resource数组之后,就开始调用XmlBeanDefinitionReader的loadBeanDefinitions方法,进行BeanDefinition的加载:

随后调用重载方法:

再看doLoadBeanDefinitions方法:

转到registerBeanDefinitions方法:

再转到DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法,最后会调用它的doRegisterBeanDefinitions(Element root)方法:

最主要的是parseBeanDefinitions方法:

其中,处理自定义节点是处理import,alias,bean,和beans节点之外的节点使用。比如最常用的compant-scan。它的处理逻辑是使用DefaultNamespaceHandlerResolver类,从Spring的META-INF/spring.handlers目录下,获取处理命名空间的类:

比如定义compant-scan的命名空间是www.springframework.org/schema/context,上图得知处理该命名空间的类是ContextNamespaceHandler:

发现处理component-scan的类是ComponentScanBeanDefinitionParser,ComponentScanBeanDefinitionParser类的parse方法就是自动扫描包路径并生成BeanDefinition的方法,具体细节不再赘述。

最后,终于找到了处理Bean的processBeanDefinition方法,它就是Spring用来注册BeanDefinition的核心方法:

这里使用了指派模式,指派BeanDefinitionParserDelegate类来进行xml的解析,封装成BeanDefinitionHolder对象:

 

其中,parseBeanDefinitionElement方法就是解析xml元素并生成BeanDefineNition的方法:

获取到BeanDefinitionHolder对象后,可能要对其进行装饰(暂时不知道这里是什么意思):

这里的代码暂时不考虑。

随后,就是BeanDefinition注册的最后一步了:通过BeanDefinitionRegistry注册BeanDefinition。这里是通过调用BeanDefinitionReaderUtils工具类的registerBeanDefinition方法完成的:

由于DefaultListableBeanFactory继承了BeanDefinitionRegistry,所以最后调用的是DefaultListableBeanFactory的registerBeanDefinition方法:

别名的注册暂时不分析了,以后有用到的地方再说,注册逻辑应该和名称注册逻辑差不多

接着,回到AbstractRefreshableApplicationContext的refreshBeanFactory方法:

上面的注册BeanDefineNition的逻辑都是loadBeanDefinitions做的,接下来,把DefaultListableBeanFactory对象赋值给AbstractRefreshableApplicationContext的beanFactory字段,该方法结束。

回到AbstractApplicationContext的obtainFreshBeanFactory方法:

在这里返回DefaultListableBeanFactory对象,obtainFreshBeanFactory调用结束。在obtainFreshBeanFactory调用的过程中,初始化了BeanFactory,并完成了BeanDefineNition的注册。

至此,Spring的资源加载和BeanDefineNition的注册分析完毕,下面的章节开始分析prepareBeanFactory方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值