天天看点

一文带你探究Spring|IOC原理

说到spring不得不提其两大特性IOC、AOP,本文主要介绍结合代码看下spring Ioc相关原理,阅读源码如果只是为了看源码效率就会很低,还是要有一定的目的性,我们结合着以下的问题去源码中找答案。

  • Bean工厂是如何产生Bean的
  • Bean的依赖关系是由谁来解决的
  • Bean工厂和应用上下文的区别

如下图spring Ioc的整体架构图中可以看出,Spring启动时读取bean配置信息,并在spring中生成一份相应bean配置注册表,然后会根据注册表实例化Bean,并且装配好bean 之间的依赖关系,为上层应用提供bean实例,其中bean的缓存池是通过hashmap实现的

一文带你探究Spring|IOC原理

bean的构建过程

spring.xml中保存了Bean的描述配置,BeanFactory读取这些配置然后生成bean,这是我们对ioc原理的一般理解,深入思考还会有更多问题产生

  • 哪个java对象承载了配置信息里的内容
  • 这些承载对象是读取配置文件并装载的
  • 这些装载对象又保存在哪里

BeanDefinition(Bean定义)

ioc 实现中 我们在xml 中描述的Bean信息最后 都将保存至BeanDefinition (定义)对象中,其中xml bean 与BeanDefinition 程一对一的关系。xml bean中设置的属性最后都会体现在BeanDefinition中

一文带你探究Spring|IOC原理

由此可见,xml bean中设置的属性最后都会体现在BeanDefinition中。如:

* XML-bean * BeanDefinition
class beanClassName
scope scope
lazy-init lazyInit
constructor-arg ConstructorArgument
property MutablePropertyValues
factory-method factoryMethodName
destroy-method AbstractBeanDefinition.destroyMethodName
init-method AbstractBeanDefinition.initMethodName
autowire AbstractBeanDefinition.autowireMode
id
name

BeanDefinition 属性结构

一文带你探究Spring|IOC原理

BeanDefinitionRegistry(Bean注册器)

在上表中我们并没有看到 xml bean 中的 id 和name属性没有体现在定义中,原因是ID 其作为当前Bean的存储key注册到了BeanDefinitionRegistry 注册器中。name 作为别名key 注册到了 AliasRegistry 注册中心。其最后都是指向其对应的BeanDefinition。

BeanDefinitionRegistry属性结构

一文带你探究Spring|IOC原理

BeanDefinitionReader(Bean定义读取)

现在我们了解 BeanDefinition 中存储了Xml Bean信息,而BeanDefinitionRegister 基于ID和name 保存了Bean的定义。接下要学习的是从xml Bean到BeanDefinition 然后在注册至BeanDefinitionRegister 整个过程。

一文带你探究Spring|IOC原理

上图中可以看出Bean的定义是由BeanDefinitionReader 从xml 中读取配置并构建出 BeanDefinitionReader,然后在基于别名注册到BeanDefinitionRegister中。

BeanDefinitionReader结构

一文带你探究Spring|IOC原理
  • int loadBeanDefinitions(Resource var1)

    基于资源加载beanDefinition并注册到注册器

  • int loadBeanDefinitions(String var1)

    基于资源路径加载beanDefinition并注册大屏注册器

  • BeanDefinitionRegistry getRegistry()

    获取注册器

  • ResourceLoader getResourceLoader()

    获取资源装载器

基于示例演示BeanDefinitionReader装载过程

//创建一个简单注册器
BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();
//创建bean定义读取器
BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);
// 创建资源读取器
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
// 获取资源
Resource xmlResource = resourceLoader.getResource("spring.xml");
// 装载Bean的定义
reader.loadBeanDefinitions(xmlResource);
// 打印构建的Bean 名称
System.out.println(Arrays.toString(register.getBeanDefinitionNames());
      

Beanfactory(bean 工厂)

有了Bean的定义就相当于有了产品的配方,接下来就是要把这个配方送到工厂进行生产了。在ioc当中Bean的构建是由BeanFactory 负责的。其结构如下:

一文带你探究Spring|IOC原理

方法说明:

  • getBean(String)
    • 基于ID或name 获取一个Bean
  • T getBean(Class requiredType)
    • 基于Bean的类别获取一个Bean(如果出现多个该类的实例,将会报错。但可以指定 primary=“true” 调整优先级来解决该错误 )
  • Object getBean(String name, Object… args)
    • 基于名称获取一个Bean,并覆盖默认的构造参数
  • boolean isTypeMatch(String name, Class<?> typeToMatch)
    • 指定Bean与指定Class 是否匹配

以上方法中重点要关注getBean,当用户调用getBean的时候就会触发 Bean的创建动作,其是如何创建的呢?

演示基本BeanFactory获取一个Bean

#创建Bean堆栈
// 其反射实例化Bean
java.lang.reflect.Constructor.newInstance(Unknown Source:-1)
BeanUtils.instantiateClass()
//基于实例化策略 实例化Bean
SimpleInstantiationStrategy.instantiate()
AbstractAutowireCapableBeanFactory.instantiateBean()
// 执行Bean的实例化方法
AbstractAutowireCapableBeanFactory.createBeanInstance()
AbstractAutowireCapableBeanFactory.doCreateBean()
// 执行Bean的创建
AbstractAutowireCapableBeanFactory.createBean()
// 缓存中没有,调用指定Bean工厂创建Bean
AbstractBeanFactory$1.getObject()
// 从单例注册中心获取Bean缓存
DefaultSingletonBeanRegistry.getSingleton()
AbstractBeanFactory.doGetBean()
// 获取Bean
AbstractBeanFactory.getBean()
// 调用的客户类
com.tuling.spring.BeanFactoryExample.main()
      

Bean创建时序图:

一文带你探究Spring|IOC原理

从调用过程可以总结出以下几点:

  1. 调用BeanFactory.getBean() 会触发Bean的实例化。
  2. DefaultSingletonBeanRegistry 中缓存了单例Bean
  3. Bean的创建与初始化是由AbstractAutowireCapableBeanFactory 完成的。

BeanFactory 与 ApplicationContext区别

BeanFactory 看上去可以去做IOC当中的大部分事情,为什么还要去定义一个ApplicationContext 呢?

ApplicationContext 结构图

![image-20200805111944795](/Users/book/Library/Application Support/typora-user-images/image-20200805111944795.png)

从图中可以看到 ApplicationContext 它由BeanFactory接口派生而来,因而提供了BeanFactory所有的功能。除此之外context包还提供了以下的功能:

  1. MessageSource, 提供国际化的消息访问
  2. 资源访问,如URL和文件
  3. 事件传播,实现了ApplicationListener接口的bean
  4. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层