P3:DI 依赖注入
可注入的数据类型
基本数据类型和 String、集合类型、Bean 类型。
实现方式
构造器注入:IoC Service Provider 会检查被注入对象的构造器,取得它所需要的依赖对象列表,进而为其注入相应的对象。这种方法的优点是在对象构造完成后就处于就绪状态,可以马上使用。缺点是当依赖对象较多时,构造器的参数列表会比较长,构造器无法被继承,无法设置默认值。对于非必需的依赖处理可能需要引入多个构造器,参数数量的变动可能会造成维护的困难。
setter 方法注入:当前对象只需要为其依赖对象对应的属性添加 setter 方法,就可以通过 setter 方法将依赖对象注入到被依赖对象中。setter 方法注入在描述性上要比构造器注入强,并且可以被继承,允许设置默认值。缺点是无法在对象构造完成后马上进入就绪状态。
接口注入:必须实现某个接口,这个接口提供一个方法来为其注入依赖对象。使用较少,因为它强制要求被注入对象实现不必要的接口,侵入性强。
相关注解
@Autowired
:自动按类型注入,如果有多个匹配则按照指定 Bean 的 id 查找,查找不到会报错。
@Qualifier
:在自动按照类型注入的基础上再按照 Bean 的 id 注入,给变量注入时必须搭配 @Autowired
,给方法注入时可单独使用。
@Resource
:直接按照 Bean 的 id 注入,只能注入 Bean 类型。
@Value
:用于注入基本数据类型和 String 类型。
依赖注入的过程
getBean 方法是获取 Bean 实例的方法,该方***调用 doGetBean 方法,doGetBean 真正实现向 IoC 容器获取 Bean 的功能,也是触发依赖注入的地方。如果 Bean 定义为单例模式,容器在创建之前先从缓存中查找以确保整个容器中只存在一个实例对象。如果 Bean 定义为原型模式,则容器每次都会创建一个新的实例。
具体创建 Bean 实例对象的过程由 ObjectFactory 的 createBean 方法完成,该方法主要通过 createBeanInstance 方法生成 Bean 包含的 Java 对象实例和 populateBean 方法对 Bean 属性的依赖注入进行处理。
在 createBeanInstance 方法中根据指定的初始化策略,通过简单工厂、工厂方法或容器的自动装配特性生成 Java 实例对象,对工厂方法和自动装配特性的 Bean,调用相应的工厂方法或参数匹配的构造器即可完成实例化对象的工作,但最常用的默认无参构造器需要使用 JDK 的反射或 CGLib 来进行初始化。
在 populateBean 方法中,注入过程主要分为两种情况:① 属性值类型不需要强制转换时,不需要解析属性值,直接进行依赖注入。② 属性值类型需要强制转换时,首先需要解析属性值,然后对解析后的属性值进行依赖注入。依赖注入的过程就是将 Bean 对象实例设置到它所依赖的 Bean 对象属性上,真正的依赖注入是通过 setPropertyValues 方法实现的,该方法使用了委派模式。
BeanWrapperImpl 类负责对容器完成初始化的 Bean 实例对象进行属性的依赖注入,对于非集合类型的属性,大量使用 JDK 的反射机制,通过属性的 getter 方法获取指定属性注入前的值,同时调用属性的 setter 方法为属性设置注入后的值。对于集合类型的属性,将属性值解析为目标类型的集合后直接赋值给属性。
当 Spring IoC 容器对 Bean 定义资源的定位、载入、解析和依赖注入全部完成后,就不再需要我们手动创建所需的对象,Spring IoC 容器会自动为我们创建对象并且注入好相关依赖。