Spring DI二步曲之实例化

 

什么是依赖注入?

依赖注入(Dependency Injection,DI)是我们可以用来实现 IoC 的一种模式,其中被反转的控制是设置对象的依赖关系。

将对象与其他对象连接起来,或将对象“注入”到其他对象中,是由程序完成的,而不是由对象本身完成的。

Spring中的DI

当大家开始学习spring的时候,都会从如下的示例代码开始,在前面几篇文章中详细讲解了第一行代码,spring是如何实现IoC的,现在我们要讲解spring是如何实现DI的。

 

注意:本文是以5.2.3版本为讲解。

步骤一:获取bean 实例

返回指定 bean 的一个实例,该实例可以是共享的,也可以是独立的。

此方法允许将 Spring BeanFactory 用作 Singleton 或 Prototype 设计模式的替代品。在 Singleton bean 的情况下,调用者可以保留对返回对象的引用。

该方法的具体实现是在“AbstractBeanFactory”类中实现的。

 

步骤二:获取IoC容器中指定名称的Bean实例

在这方法里调用“doGetBean”方法,这个方法才是真正向IoC容器获取Bean对象

 

步骤三:获取bean实例

这个方法代码比较多,大致业务逻辑如下:

  • transformedBeanName:获取bean 名称,必要时去除工厂取消引用前缀“&”,并将别名解析为规范名称

  • geetSingleton:获取指定名称的单例对象。检查已经实例化的单例,还允许对当前创建的单例进行早期引用(解决循环引用)

  • getObjectForBeanInstance:如果单例对象非空,获取bean 实例的对象,bean 实例本身或其创建的对象(如果是 FactoryBean)

  • 检查当前bean工厂是否包含该bean,如果没有找到检查父级容器中是否包含该bean,如果父级容器还找不到,使用显式参数委托给父级容器查找

  • markBeanAsCreated:将指定的 bean 标记为已创建(或即将创建)。这允许 bean 工厂优化其缓存以重复创建指定的 bean。

  • getMergedLocalBeanDefinition:获取一个RootBeanDefinition,如果指定的 bean 对应于子 bean 定义,则遍历父 bean 定义

  • checkMergedBeanDefinition:判断RootBeanDefinition是抽象类,则抛异常

  • getDependsOn:获取当前Bean所依赖Bean的名称,递归调用getBean方法进行注册

  • 如果是单例模式(singleton),通过“getSingleton”方法获取名称注册的(原始)单例对象,如果尚未注册,则通过“createBean”方法创建并注册一个新对象

  • 如果是原型模式(prototype),通过“beforePrototypeCreation”方法实现将原型注册为当前正在创建中,然后通过“createBean”方法创建并注册一个新对象,通过“afterPrototypeCreation”方法执行创建原型后的回调,将原型标记为不再处于创建状态,最后通过“getObjectForBeanInstance”方法获取给定 bean 实例的对象。

  • 如果既不是单例模式也不是原型模式,则根据Bean配置的生命周期,实例化Bean对象,如:request、session、application等生命周期

  • 检查所需类型是否与实际 bean 实例的类型匹配,由于requiredType为空,这里不做考虑

在这里我们重点看下“createBean”方法,在这里会创建一个bean实例。

 

步骤四:创建一个 bean 实例

大致业务逻辑如下:

  • 为给定的合并 bean 定义(和参数)创建一个 bean 实例。如果是子定义,bean 定义将已经与父定义合并。

  • 所有 bean 检索方法都委托给此方法以进行实际的 bean 创建。

  • 此类的中心方法:创建 bean 实例、填充 bean 实例、应用后处理器等。

在这个方法中我们看到一个以“doXXX”开头的方法,他就是真正创建实例的方法。这个方法大致业务逻辑如下:

  • resolveBeanClass:判断需要创建的bean是否可以实例化,即是否可以通过当前的类加载器加载

  • prepareMethodOverrides:验证并准备 bean 定义的方法覆盖

  • resolveBeforeIInstantiation:如果该bean配置了实例化前后处理器,则BeanPostProcessors返回一个代理对象而不是bean实例,注意在这里spring实现了AOP

  • doCreateBean:实际创建指定的bean实例

 

步骤五:实际创建指定的bean

此时已经进行了预创建处理,例如检查 postProcessBeforeInstantiation 回调。区分默认 bean 实例化、使用工厂方法和自动装配构造函数。

大致业务逻辑如下:

  • createBeanInstance:把Bean对象封装成BeanWrapper对象

  • applyMergedBeanDefiinitionPostProcessors:应用后置处理器

  • addSingletonFactory:向容器中缓存单例对象来防止循环引用

  • populateBean:使用 bean 定义中的属性值填充 BeanWrapper 中的 bean 实例

  • initializeBean:初始化实例

  • 获取已经注册单例模式的bean对象,如果根据名称获取的已注册的bean和正在实例化的bean是同一个,那么当前实例化的bean初始化完成;否则,当前bean依赖其他bean,并且当发生循环引用时不允许新创建实例对象,获取当前bean所依赖的其他bean,对依赖bean进行类型检查

  • 将 bean 添加到容器中

 

 

步骤六:把Bean对象封装成BeanWrapper对象

使用适当的实例化策略为指定的 bean 创建一个新实例:工厂方法、构造函数自动装配或简单实例化。

大致业务逻辑如下:

  • resolveBeanClass:确保此时实际解析了 bean 类

  • obtainFromSupplier:从给定的supplier获取一个 bean 实例

  • instantiateUsingFactoryMethod:使用工厂方法获取一个 bean 实例

  • autowireConstructor:按照参数类型匹配bean的构造方法获取一个bean实例

  • determiineConstructorsFromBeanPostProcessors:确定首选的构造函数

  • autowireConstructor:使用首选的构造函数获取一个bean实例

  • instantiateBean:使用无参构造函数获取一个bean实例

 

步骤七:使用默认构造函数实例化 bean

大致业务逻辑如下:

  • 获取系统的安全管理接口非空,使用匿名内部类,根据实例化策略创建实例对象;否则使用默认策略来创建实例,默认策略类是SimpleInstantiationStrategy类

  • initBeanWrapper:使用在该工厂注册的自定义编辑器初始化给定的 BeanWrapper

 

步骤八:使用初始化策略实例化bean对象

这个方法业务很简单:如果bean有方法被覆盖了,则使用JDK的反射机制进行实例化,否则,使用CGLib进行实例化。

 

步骤九:使用JDK的反射机制实例化bean对象

 

步骤十:使用CGLib实例化bean对象

大致业务逻辑如下:

  • createEnhancedSubclass:使用 CGLIB 为提供的 bean 定义创建 bean 类的增强子类

  • Bean.instantiateClass:如果构造器为空,使用JDK的反射机制实例化bean对象

  • enhancedSubclassConstructor:使用CGLib实例化bean对象

 

 

步骤十一:使用 bean 定义中的属性值填充 BeanWrapper 中的 bean 实例

这个方法虽然代码量很多,其实业务很简单:

  • 获取容器在解析Bean定义资源时为BeanDefinition中设置的属性值PropertyValues

  • applyPropertyValues:对属性进行注入

步骤十二:解析并注入依赖属性

应用给定的属性值,解析对此 bean 工厂中其他 bean 的任何运行时引用。必须使用深拷贝,所以我们不会永久修改这个属性。

分析下面代码,我们可以看出,对属性的注入过程分以下两种情况:

  • 属性值类型不需要强制转换时,不需要解析属性值,直接准备进行依赖注入

  • 属性值需要进行类型强制转换时,如对其他对象的引用等,首先需要解析属性值,然后对解析后的 属性值进行依赖注入

对属性值的解析是在 BeanDefinitionValueResolver 类中的 resolveValueIfNecessary()方法中进行的, 对属性值的依赖注入是通过 bw.setPropertyValues()方法实现的。

 

 

步骤十三:解析属性注入规则

在这个方法中我们看到对多种情况进行解析,比如:对引用类型的属性进行解析(RuntimeBeanReference)、对属性值是引用容器中另一个Bean名称的解析(RuntimeBeanNameReference)、对Bean类型属性的解析,主要是Bean中的内部类(BeanDefinitionHolder)、对集合数组类型的属性解析(ManagedArray)等等。

 

通过上面的代码分析,我们明白了 Spring 是如何将引用类型,内部类以及集合类型等属性进行解析的, 属性值解析完成后就可以进行依赖注入了,依赖注入的过程就是 Bean 对象实例设置到它所依赖的 Bean 对象属性上去。

下篇文章将将讲解真正的依赖注入(bw.setPropertyValues),该方法使用了委托模式。

时序图

 

写在最后

好兄弟可以点赞并关注我的公众号“javaAnswer”,全部都是干货。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件求生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值