DI
DI(Dependency Injection)依赖注入:就是指对象是被动接受依赖类而不是自己主动去找,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。
依赖注入发生的时间当 Spring IOC 容器完成了 Bean 定义资源的定位、载入和解析注册以后,IOC 容器中已经管理类 Bean定义的相关数据,但是此时 IOC 容器还没有对所管理的 Bean 进行依赖注入,依赖注入在以下两种情况发生:
1)、用户第一次调用 getBean()方法时,IOC 容器触发依赖注入。
2)、当用户在配置文件中将<bean>元素配置了 lazy-init=false 属性,即让容器在解析注册 Bean 定义时进行预实例化,触发依赖注入。
BeanFactory 接口定义了 Spring IOC 容器的基本功能规范,是 Spring IOC 容器所应遵守的最底层和最基本的编程规范。BeanFactory 接口中定义了几个 getBean()方法,就是用户向 IOC 容器索取管理的Bean 的方法,我们通过分析其子类AbstractBeanFactory 的具体实现,理解 Spring IOC 容器在用户索取 Bean 时如何完成依赖注入。
AbstractBeanFactory 的 getBean()相关方法的源码如下:
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 | |
通过上面对向 IOC 容器获取 Bean 方法的分析,我们可以看到在 Spring 中,如果 Bean 定义的单例模式(Singleton),则容器在创建之前先从缓存中查找,以确保整个容器中只存在一个实例对象。如果 Bean定义的是原型模式(Prototype),则容器每次都会创建一个新的实例对象。除此之外,Bean 定义还可以通过其指定的生命周期范围来创建。
上面的源码只是定义了根据 Bean 定义的模式,采取的不同创建 Bean 实例对象的策略,具体的 Bean实例 对象的创 建过程 由实现了AbstractBeanFactory接口 的匿名内 部类的createBean()方法 完成,AbstractBeanFactory使 用 委 派 模 式 , 具 体 的 Bean 实 例 创 建 过 程 交 由 其 实 现 类AbstractAutowireCapableBeanFactory 完成,我们继续分析AbstractAutowireCapableBeanFactory的 createBean()方法的源码,理解其创建 Bean 实例的具体实现过程。
开始实例化AbstractAutowireCapableBeanFactory 类实现了 AbstractBeanFactory接口,创建容器指定的 Bean 实例对象,同时还对创建的 Bean 实例对象进行初始化处理。其创建 Bean 实例对象的方法源码如下
通过上面的源码注释,我们看到具体的依赖注入实现其实就在以下两个方法中:
1)、createBeanInstance()方法,生成 Bean 所包含的 java 对象实例。
2)、populateBean()方法,对 Bean 属性的依赖注入进行处理。
下面继续分析这两个方法的代码实现。
选择 Bean 实例化策略在 createBeanInstance()方法中,根据指定的初始化策略,使用简单工厂、工厂方法或者容器的自动装配特性生成 Java 实例对象,创建对象的源码如下:
0102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 | |
经过对上面的代码分析,我们可以看出,对使用工厂方法和自动装配特性的 Bean 的实例化相当比较清楚,调用相应的工厂方法或者参数匹配的构造方法即可完成实例化对象的工作,但是对于我们最常使用的默认无参构造方法就需要使用相应的初始化策略(JDK 的反射机制或者 CGLib)来进行初始化了,在方法 getInstantiationStrategy().instantiate()中就具体实现类使用初始策略实例化对象。
执行 Bean 实例化在使用默认的无参构造方法创建 Bean 的实例化对象时,方法 getInstantiationStrategy().instantiate()调用了 SimpleInstantiationStrategy 类中的实例化 Bean 的方法,其源码如下
通过上面的代码分析,我们看到了如果 Bean 有方法被覆盖了,则使用 JDK 的反射机制进行实例化,否则,使用 CGLib 进行实例化。instantiateWithMethodInjection() 方 法 调 用SimpleInstantiationStrategy 的 子 类CGLibSubclassingInstantiationStrategy 使用 CGLib 来进行初始化,其源码如下:
01020304050607080910111213141516171819202122232425262728293031323334353637383940414243 | |
CGLib 是一个常用的字节码生成器的类库,它提供了一系列 API 实现 Java 字节码的生成和转换功能。JDK 的动态代理只能针对接口,如果一个类没有实现任何接口,要对其进行动态代理只能使用 CGLib。
准备依赖注入在前面的分析中我们已经了解到 Bean 的依赖注入主要分为两个步骤,首先调用 createBeanInstance()方法生成 Bean 所包含的 Java 对象实例。然后,调用 populateBean()方法,对 Bean 属性的依赖注入进行处理。
上面我们已经分析了容器初始化生成 Bean 所包含的 Java 实例对象的过程,现在我们继续分析生成对象后,Spring IOC 容器是如何将 Bean 的属性依赖关系注入 Bean 实例对象中并设置好的,回到AbstractAutowireCapableBeanFactory 的 populateBean()方法,对属性依赖注入的代码如下:
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 | |
分析上述代码,我们可以看出,对属性的注入过程分以下两种情况:
1)、属性值类型不需要强制转换时,不需要解析属性值,直接准备进行依赖注入。
2)、属性值需要进行类型强制转换时,如对其他对象的引用等,首先需要解析属性值,然后对解析后的属性值进行依赖注入。
对属性值的解析是在 BeanDefinitionValueResolver 类中的 resolveValueIfNecessary()方法中进行的,对属性值的依赖注入是通过 bw.setPropertyValues()方法实现的,在分析属性值的依赖注入之前,我们先分析一下对属性值的解析过程
解析属性注入规则当容器在对属性进行依赖注入时,如果发现属性值需要进行类型转换,如属性值是容器中另一个 Bean实例对象的引用,则容器首先需要根据属性值解析出所引用的对象,然后才能将该引用对象注入到目标实例对象的属性上去,对属性进行解析的由 resolveValueIfNecessary()方法实现。
Spring IOC 容器是如何将属性的值注入到 Bean 实例对象中去的:
1)、对于集合类型的属性,将其属性值解析为目标类型的集合后直接赋值给属性。
2)、对于非集合类型的属性,大量使用了 JDK 的反射机制,通过属性的 getter()方法获取指定属性注入以前的值,同时调用属性的 setter()方法为属性设置注入后的值。看到这里相信很多人都明白了 Spring的 setter()注入原理
Spring DI运行时序图
<ignore_js_op>