spring源码解析_《Spring源码解析(七)》从源码深处体验Spring核心技术--Spring自动装配之依赖注入...

首先来看一张Bean的解析过程图【需要高清大图可以找我哦】

0f75673b2c13c48001f5d35ff0d8f3f9.png

依赖注入发生的时间

当 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 的方法,我们通过分析其子类的具体实现,理解 Spring IOC 容器在用户索取 Bean 时如何完成依赖注入。

aa8970573d0dc3bb3a4ac8e2ef97d0d9.png

在 BeanFactory 中我们可以看到 getBean(String...)方法,但它具体实现在AbstractBeanFactory 中。

寻找获取 Bean 的入口

AbstractBeanFactory 的 getBean()相关方法的源码如下:

e9e0bff501d6e78f95da82f26e6c626d.png

a46caec7d94a1b80c5a0b2c0482da040.png

f558cfce71ed34f49643b863d3a842b0.png

7bd956a84f2b2e2fdb366480cd9df044.png

eb87f32dc8792b1465f44d83c3cfa42b.png

7cb4bf54d8a927225b9ce0e562b13e90.png

通过上面对向 IOC 容器获取 Bean 方法的分析,我们可以看到在 Spring 中,如果 Bean 定义的单例模式(Singleton),则容器在创建之前先从缓存中查找,以确保整个容器中只存在一个实例对象。

如果 Bean定义的是原型模式(Prototype),则容器每次都会创建一个新的实例对象。

除此之外,Bean 定义还可以扩展为指定其生命周期范围。

上面的源码只是定义了根据 Bean 定义的模式,采取的不同创建 Bean 实例对象的策略,具体的Bean实例 对象的创 建过程 由实现了ObjectFactory接口的匿名内部类的 createBean()方法 完成,ObjectFactory 使 用 委 派 模 式 , 具 体 的 Bean 实 例 创 建 过 程 交 由 其 实 现 类 AbstractAutowireCapableBeanFactory 完成,我们继续分析AbstractAutowireCapableBeanFactory的 createBean()方法的源码,理解其创建 Bean 实例的具体实现过程。

开始实例化

AbstractAutowireCapableBeanFactory 类实现了 ObjectFactory 接口,创建容器指定的 Bean 实例对象,同时还对创建的 Bean 实例对象进行初始化处理。其创建 Bean 实例对象的方法源码如下:

2f55b7fdac95a2ea59fb85dfcf2defd3.png

e9fba5062ae8a9a85f0f56a5efa379d0.png

bb8533390866da8a341007f55ea36d70.png

55ee87223990827b9c8b19e7b8aeffc0.png

e6d1e3679a07a2fe8b194211bd979995.png

通过上面的源码注释,我们看到具体的依赖注入实现其实就在以下两个方法中:

1)、createBeanInstance()方法,生成 Bean 所包含的 java 对象实例。

2)、populateBean()方法,对 Bean 属性的依赖注入进行处理。

下面继续分析这两个方法的代码实现。

选择 Bean 实例化策略

在 createBeanInstance()方法中,根据指定的初始化策略,使用简单工厂、工厂方法或者容器的自动装配特性生成 Java 实例对象,创建对象的源码如下:

454a5302bbaff971f0e9d7fc8b57f3bb.png

b47ed1f54088ed0401de9388eeeeab74.png

1b9fe3d9679ab851eee0205cc0d9520c.png

经过对上面的代码分析,我们可以看出,对使用工厂方法和自动装配特性的 Bean 的实例化相当比较清楚,调用相应的工厂方法或者参数匹配的构造方法即可完成实例化对象的工作,但是对于我们最常使用的默认无参构造方法就需要使用相应的初始化策略(JDK 的反射机制或者 CGLib)来进行初始化了,在方法 getInstantiationStrategy().instantiate()中就具体实现类使用初始策略实例化对象。

执行 Bean 实例化

在使用默认的无参构造方法创建 Bean 的实例化对象时,方法getInstantiationStrategy().instantiate()调用了 SimpleInstantiationStrategy 类中的实例化 Bean 的方法,其源码如下:

25948eba54ffb3f59d35d180a1f07da5.png

8e6608e7421a94ae1b31d27a423beacb.png

通过上面的代码分析,我们看到了如果 Bean 有方法被覆盖了,则使用 JDK 的反射机制进行实例化,否则,使用 CGLib 进行实例化。

instantiateWithMethodInjection()方法用SimpleInstantiationStrategy的子类 CGLibSubclassingInstantiationStrategy 使用 CGLib 来进行初始化,其源码如下:

bce807ce3ba234a4c742c666afebb9d2.png

f91f0dd75eb90e292e51955c83c20a0f.png

CGLib 是一个常用的字节码生成器的类库,它提供了一系列 API 实现 Java 字节码的生成和转换功能。

我们在学习 JDK 的动态代理时都知道,JDK 的动态代理只能针对接口,如果一个类没有实现任何接口,要对其进行动态代理只能使用 CGLib。

准备依赖注入

在前面的分析中我们已经了解到 Bean 的依赖注入主要分为两个步骤,首先调用 createBeanInstance()方法生成 Bean 所包含的 Java 对象实例。然后,调用 populateBean()方法,对 Bean 属性的依赖注入进行处理。

上面我们已经分析了容器初始化生成 Bean 所包含的 Java 实例对象的过程,现在我们继续分析生成对象后,Spring IOC 容器是如何将 Bean 的属性依赖关系注入 Bean 实例对象中并设置好的,回到AbstractAutowireCapableBeanFactory 的 populateBean()方法,对属性依赖注入的代码如下:

3f2ea5cd0d929b93295d9cfb9862b57e.png

b3f2e5cb8ed482815c8fb493fb65a406.png

1853e0b2d76337dce799ab11264f993b.png

633cf5d17a558bf1f43e509d6f107471.png

493cba777663abd8ed4110748c090c60.png

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

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

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

对属性值的解析是在 BeanDefinitionValueResolver 类中的 resolveValueIfNecessary()方法中进行的,对属性值的依赖注入是通过 bw.setPropertyValues()方法实现的,在分析属性值的依赖注入之前,我们先分析一下对属性值的解析过程。

解析属性注入规则

当容器在对属性进行依赖注入时,如果发现属性值需要进行类型转换,如属性值是容器中另一个 Bean实例对象的引用,则容器首先需要根据属性值解析出所引用的对象,然后才能将该引用对象注入到目标实例对象的属性上去,对属性进行解析的由 resolveValueIfNecessary()方法实现,其源码如下:

2f2174c55d10a888a7a54b20062dad1e.png

a78dd6bce617b35ce096431b9ca9e11a.png

f1fc53bf04c79775fda7683e17d11fbe.png

bbef06e2d82f3df5c60b6c3a4766b1d8.png

af0372d3e30cd0f56c6e768c02229dc4.png

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

而真正的依赖注入是通过 bw.setPropertyValues()方法实现的,该方法也使用了委托模式 , 在 BeanWrapper 接 口 中 至 少 定 义 了 方 法 声 明 , 依 赖 注 入 的 具 体 实 现 交 由 其 实 现 类BeanWrapperImpl 来完成,下面我们就分析依 BeanWrapperImpl 中赖注入相关的源码。

注入赋值

BeanWrapperImpl 类主要是对容器中完成初始化的 Bean 实例对象进行属性的依赖注入,即把 Bean对象设置到它所依赖的另一个 Bean 的属性中去。

然而,BeanWrapperImpl 中的注入方法实际上由AbstractNestablePropertyAccessor 来实现的,其相关源码如下:

a3939a53f87133de9d59686f46734876.png

d7a3aaec783b30f280aaa15b4c3c477b.png

c4ab46657b310f2f5f9e48045fa59bd7.png

89a0cb1738cbcefe42db11dc17114309.png

通过对上面注入依赖代码的分析,我们已经明白了 Spring IOC 容器是如何将属性的值注入到 Bean 实例对象中去的:

1)、对于集合类型的属性,将其属性值解析为目标类型的集合后直接赋值给属性。

2)、对于非集合类型的属性,大量使用了 JDK 的反射机制,通过属性的 getter()方法获取指定属性注入以前的值,同时调用属性的 setter()方法为属性设置注入后的值。

看到这里相信很多人都明白了Spring的 setter()注入原理。

如果你能认真的看完,那么看到这个时序图,你一定很熟悉,跟着图在来理解一遍,更加深刻。(高清大图,评论【DI】即可)

07a88e7ed90e94cdaf6b445dc306251c.png

至此 Spring IOC 容器对 Bean 定义资源文件的定位,载入、解析和依赖注入已经全部分析完毕。

现在Spring IOC 容器中管理了一系列靠依赖关系联系起来的 Bean,程序不需要应用自己手动创建所需的对象,Spring IOC 容器会在我们使用的时候自动为我们创建,并且为我们注入好相关的依赖,这就是Spring 核心功能的控制反转和依赖注入的相关功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值