了解了依赖注入的相关知识之后,来研究一下源码中具体的实现。
一、依赖注入的实现
依赖注入大体分以下几个步骤:
- AbstractBeanFactory中的getBean方法向IoC容器获取被管理的Bean
- AbstractAutowireCapableBeanFactory的createBean方法创建Bean实例对象
- createBeanInstance方法创建Bean的java实例对象
- SimpleInstantiationStrategy类的instantiate方法使用默认的无参构造方法创建Bean实例化对象
- populateBean方法对Bean属性的依赖注入
- BeanDefinitionValueResolver解析属性值
- BeanWrapperImpl对Bean属性的依赖注入
1、AbstractBeanFactory中的getBean方法
最开始就说过,getBean是在最基本的IOc容器BeanFactory接口中就定义的方法,它的默认实现类AbstractBeanFactory实现了这个方法,而更高级的ApplicationContext的实现类使用的都是DefaultListableBeanFactory,它也是继承自AbstractBeanFactory的,继承关系如下:
看一下AbstractBeanFactory中的getBean方法:
1 | //获取IoC容器中指定名称的Bean |
可以看出
- 如果bean设置的是singleton的,就先从缓存中查找,因为单例的只创建一个,如果不是,就开始创建
- 获取当前Bean所有依赖的Bean,递归调用getBean方法,直道取到一个没有任何依赖的Bean
- 判断Bean的属性
- singleton,单例模式的
- prototype,原型模式的,每次都返回一个新的实例
- 其他,如:request、session、application
- 不管是那种模式,都调用了createBean方法,来获取bean实例,不过返回的是Object对象
- 最后返回指定类型的bean实例
createBean方法是在父类AbstractAutowireCapableBeanFactory中实现的
2、AbstractAutowireCapableBeanFactory的createBean方法
createBean方法:
1 | //创建Bean实例对象 |
创建容器指定的Bean实例对象,同时还对创建的Bean实例对象进行初始化处理比如init-method、后置处理器等。
关键步骤由以下两个方法实现:
- createBeanInstance:生成Bean所包含的Java对象实例。
- populateBean :对Bean属性的依赖注入进行处理。
这两个方法也在这个类中,下面继续分析
3、createBeanInstance方法
3.1、createBeanInstance方法
1 | //创建Bean的实例对象 |
可以看出,bean的实例化分为很多种情况,
- 对于使用工厂方法和自动装配特性的bean的实例化,则调用对应的工厂方法或者参数匹配的构造方法即可完成实例化对象的工作
- 对于最常使用的无参构造方法,则使用相应的初始化策略
- JDK的反射机制
- 或者CGLIB
关于Spring中Bean实例化的多种方式上面已经介绍过,下面主要看一下最常用的无参构造方法实例化bean的过程,这个方法在SimpleInstantiationStrategy类中的instantiate方法中实现。
4、SimpleInstantiationStrategy的instantiate方法
4.1、instantiate方法
代码如下:
1 | //使用初始化策略实例化Bean对象 |
这个Strategy是Spring中用来生成Bean对象的默认类,它提供了两种实例化Java对象的方法:
- 一种是通过BeanUtils,使用JVM的反射机制
- 一种是通过CGLIB来生成。
判断的条件就是Bean定义中有没有方法覆盖。
4.2、反射还是CGLIB
关于这个条件,开始不是很明白,书上也没有解释为什么通过这个条件来判断,所以上网查了一些资料
程序中首先判断如果beanDefinition.getMethodOverrides()为空,也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷,但如果使用了这两个特性,再直接使用反射的方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性锁对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例。
那么是什么条件才会触发这个MethodOverrides呢?
其实是Spring配置文件中的lookup-method和replace-method,这其实是两个方法级别的注入,和一般的属性(Property)注入是不一样的,它们注入的是方法(Method)。具体的介绍在依赖注入的基础部分已经介绍过了。
4.3、CGLIB与instantiateWithMethodInjection方法
instantiateWithMethodInjection方法在SimpleInstantiationStrategy的子类CglibSubclassingInstantiationStrategy中:
1 | //使用CGLIB进行Bean对象实例化 |
CGLIB是一个常用的字节码生成器的类库,它提供了一系列API实现java字节码的生成和转换功能。我们在学习JDK的动态代理时都知道,JDK的动态代理只能针对接口,如果一个类没有实现任何接口,要对其进行动态代理只能使用CGLIB。
至此,Bean的Java对象就已经生成了,而生成之后,还需要对它进行依赖关系的设置,就是最后一步。
5、populateBean方法
1 | //将Bean属性设置到生成的实例对象上 |
可以看出,该方法过程主要如下:
- 对要注入的属性进行判断:
- 属性值类型不需要转换时,不需要解析属性值,直接准备进行依赖注入。
- 属性值需要进行类型转换时,如对其他对象的引用等,首先需要解析属性值,然后对解析后的属性值进行依赖注入。解析时需要调用BeanDefinitionValueResolver类的resolveValueIfNecessary方法。
- BeanWrapperImpl中的bw.setPropertyValues来实现依赖注入
下面分别看一下这两个方法
6、BeanDefinitionValueResolver解析属性值
当容器在对属性进行依赖注入时,如果发现属性值需要进行类型转换,如属性值是容器中另一个Bean实例对象的引用,则容器首先需要根据属性值解析出所引用的对象,然后才能将该引用对象注入到目标实例对象的属性上去,对属性进行解析的由resolveValueIfNecessary方法实现,其源码如下:
1 | //解析属性值,对注入类型进行转换 |
可以看出,对应不同类型的属性,调用不同的方法进行解析:
1 | //解析引用类型的属性值 |
解析结束之后就是最后的注入过程了。
7、BeanWrapperImpl对Bean属性的依赖注入
BeanWrapperImpl类主要是对容器中完成初始化的Bean实例对象进行属性的依赖注入,即把Bean对象设置到它所依赖的另一个Bean的属性中去,依赖注入的相关源码如下:
1 | //实现属性依赖注入功能 |
通过对上面注入依赖代码的分析,我们已经明白了Spring IoC容器是如何将属性的值注入到Bean实例对象中去的:
- 对于集合类型的属性,将其属性值解析为目标类型的集合后直接赋值给属性。
- 对于非集合类型的属性,大量使用了JDK的反射和内省机制,通过属性的getter方法(reader method)获取指定属性注入以前的值,同时调用属性的setter方法(writer method)为属性设置注入后的值。看到这里相信很多人都明白了Spring的setter注入原理。
二、总结
再回顾一下整个依赖注入的过程:
AbstractBeanFactory中的getBean方法来获取Bean:
在缓存中查找,如果存在则直接返回,否则开始下一步创建Bean
AbstractAutowireCapableBeanFactory类中创建Bean,这个类中有几个关键方法:
createBean方法:创建容器指定的Bean实例对象的入口
同时还对创建的Bean实例对象进行初始化处理比如init-method、后置处理器等,然后调用下面的两个方法创建实例并注入依赖
createBeanInstance方法:创建Bean的Java实例对象,分两种情况
- 对于使用工厂方法和自动装配特性的bean的实例化:则调用对应的工厂方法或者参数匹配的构造方法即可完成实例化对象的工作
- 否则调用默认的无参构造器进行实例化即SimpleInstantiationStrategy的instantiate方法,
- 使用Java的反射技术
- 使用CGLIB
populateBean方法:实例化之后,根据属性类型决定是否需要解析,最后通过BeanWrapperImpl类完成对属性的注入,对属性的类型也要进行判断:
- 属性值类型不需要转换时,不需要解析属性值,直接准备进行依赖注入
- 属性值需要进行类型转换时,如对其他对象的引用等,首先需要解析属性值,然后对解析后的属性值进行依赖注入。解析过程由BeanDefinitionValueResolver类的setPropertyValue方法完成
BeanWrapperImpl对Bean属性的依赖注入:
- 对于集合类属性,将其属性值解析为目标类型的集合后直接赋值给属性。
- 对于非集合类型的属性,大量使用了JDK的反射和内省机制,通过属性的getter方法(reader method)获取指定属性注入以前的值,同时调用属性的setter方法(writer method)为属性设置注入后的值。
虽然上面分析了这么多,但核心过程就是上面的这几句话。
三、参考地址
http://www.cnblogs.com/ITtangtang/p/3978349.html