什么是依赖注入?
依赖注入是我们可以用来实现 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 实例本身或其创建的对象
检查当前bean工厂是否包含该bean,如果没有找到检查父级容器中是否包含该bean,如果父级容器还找不到,使用显式参数委托给父级容器查找
markBeanAsCreated:将指定的 bean 标记为已创建。这允许 bean 工厂优化其缓存以重复创建指定的 bean。
getMergedLocalBeanDefinition:获取一个RootBeanDefinition,如果指定的 bean 对应于子 bean 定义,则遍历父 bean 定义
checkMergedBeanDefinition:判断RootBeanDefinition是抽象类,则抛异常
getDependsOn:获取当前Bean所依赖Bean的名称,递归调用getBean方法进行注册
如果是单例模式,通过“getSingleton”方法获取名称注册的单例对象,如果尚未注册,则通过“createBean”方法创建并注册一个新对象
如果是原型模式,通过“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方法实现的。
步骤十三:解析属性注入规则
在这个方法中我们看到对多种情况进行解析,比如:对引用类型的属性进行解析、对属性值是引用容器中另一个Bean名称的解析、对Bean类型属性的解析,主要是Bean中的内部类、对集合数组类型的属性解析等等。
通过上面的代码分析,我们明白了 Spring 是如何将引用类型,内部类以及集合类型等属性进行解析的, 属性值解析完成后就可以进行依赖注入了,依赖注入的过程就是 Bean 对象实例设置到它所依赖的 Bean 对象属性上去。
下篇文章将将讲解真正的依赖注入,该方法使用了委托模式。
时序图
写在最后
好兄弟可以点赞并关注我的公众号“javaAnswer”,全部都是干货。