4、IOC 之Bean的依赖关系

4.1、依赖注入(DI)

依赖关系注入 (DI) 是一个过程,其中对象仅通过构造函数参数、工厂方法的参数或在对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖关系(即,它们与之一起工作的其他对象)。然后,容器在创建 Bean 时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转)

DI 有两种主要变体:基于构造函数的依赖关系注入基于 Setter 的依赖关系注入

① 基于构造函数的依赖关系注入

1、构造函数参数解析

基于构造函数的 DI 是通过容器调用具有许多参数的构造函数来完成的,每个参数表示一个依赖项。调用具有特定参数的工厂方法来构造 Bean 几乎是等效的。下面的示例演示一个只能使用构造函数注入进行依赖关系注入的类

Bean类:

package hom.wang.vo;
import lombok.Data;
@Data
public class ExampleBean {private String name;private int age;private int money;private BeanA beanA;private BeanB beanB;public ExampleBean(String name, int age, int money, BeanA beanA, BeanB beanB) {this.name = name;this.age = age;this.money = money;this.beanA = beanA;this.beanB = beanB;}
}// 同包下BeanA和BeanB
public class BeanA {}
public class BeanB {}

xml定义Bean:

构造函数参数解析匹配通过使用参数的类型解析。如果 Bean 定义的构造函数参数中不存在潜在的歧义,则在 Bean 定义中定义构造函数参数的顺序就是在实例化 Bean 时将这些参数提供给相应构造函数的顺序。

由于 BeanA 和 BeanB类不通过继承相关,则不存在潜在的歧义。因此,以下配置工作正常,并且不需要在元素中显式指定构造函数参数索引或类型

<beans><bean id="beanA" class="hom.wang.vo.BeanA"/><bean id="beanB" class="hom.wang.vo.BeanB"/><bean id="exampleBean" class="hom.wang.vo.ExampleBean"><constructor-arg value="张三"/><constructor-arg value="20"/><constructor-arg value="30"/><constructor-arg ref="beanA"/><constructor-arg ref="beanB"/></bean>
</beans>

2、构造函数参数类型匹配

通过 type 指定参数类型,避免相同类型的参数多义性(然并卵,不看类实现你依然不知道每个属性是神马含义)

xml定义Bean(沿用上面①的Bean类):

<beans><bean id="beanA" class="hom.wang.vo.BeanA"/><bean id="beanB" class="hom.wang.vo.BeanB"/><bean id="exampleBean" class="hom.wang.vo.ExampleBean"><constructor-arg type="java.lang.String" value="张三"/><constructor-arg type="int" value="20"/><constructor-arg type="int" value="30"/><constructor-arg type="hom.wang.vo.BeanA" ref="beanA"/><constructor-arg type="hom.wang.vo.BeanB" ref="beanB"/></bean>
</beans>

3、构造函数参数索引

通过 index 显式指定构造函数参数的索引(索引从 0 开始)

除了解决多个简单值的多义性之外,指定索引还可以解决构造函数具有两个相同类型的参数的多义性

(然并卵,不看类具体定义你依然不知道每个属性是神马含义)

xml定义Bean(沿用上面①的Bean类):

<beans><bean id="beanA" class="hom.wang.vo.BeanA"/><bean id="beanB" class="hom.wang.vo.BeanB"/><bean id="exampleBean" class="hom.wang.vo.ExampleBean"><constructor-arg index="0" value="张三"/><constructor-arg index="1" value="20"/><constructor-arg index="2" value="30"/><constructor-arg index="3" ref="beanA"/><constructor-arg index="4" ref="beanB"/></bean>
</beans>

4、构造函数参数名称

通过 name 指定参数名称来消除歧义

xml定义Bean(沿用上面①的Bean类):

<beans><bean id="beanA" class="hom.wang.vo.BeanA"/><bean id="beanB" class="hom.wang.vo.BeanB"/><bean id="exampleBean" class="hom.wang.vo.ExampleBean"><constructor-arg name="name" value="张三"/><constructor-arg name="age" value="20"/><constructor-arg name="money" value="30"/><constructor-arg name="beanA" ref="beanA"/><constructor-arg name="beanB" ref="beanB"/></bean>
</beans>

**【 MISUNDERSTAND:摘自官网,小白也不清楚,先记录一波】**请记住,要开箱即用,必须在启用调试标志的情况下编译代码,以便Spring可以从构造函数中查找参数名称。如果不能或不想使用调试标志编译代码,则可以使用 JDK提供的注解 @ConstructorProperties 显式命名构造函数参数。

@ConstructorProperties({"name", "age", "money", "beanA", "beanB"})
public ExampleBean(String name, int age, int money, BeanA beanA, BeanB beanB) {// ...
}

② 基于 setter 的依赖关系注入

基于 Setter 的 DI 是在调用无参数构造函数或无参数工厂方法来实例化 Bean 之后,通过容器调用 Bean 上的 setter 方法来完成的(用 <property/> 指定属性)。

xml定义Bean(沿用上面①的Bean类):

<beans><bean id="beanA" class="hom.wang.vo.BeanA"/><bean id="beanB" class="hom.wang.vo.BeanB"/><bean id="exampleBean" class="hom.wang.vo.ExampleBean"><property name="name" value="张三"/><property name="age" value="20"/><property name="money" value="30"/><property name="beanA" ref="beanA"/><property name="beanB" ref="beanB"/></bean>
</beans>

基于构造函数还是基于设置器的 DI?

由于可以混合使用基于构造函数和基于 setter 的 DI,因此将构造函数用于强制依赖项,而将 setter 方法或 Java配置方法用于可选依赖项,这是一个很好的经验法则。请注意,在 setter 方法上使用 @Required注释可用于使属性成为必需的依赖项,但是,最好使用带有编程验证参数的构造函数注入。

【 MISUNDERSTAND:摘自官网,编程验证?嘛意思?也许是@NotNull这种】

依赖关系解析过程(摘自官网可略过)

容器按如下方式执行 Bean 依赖关系解析:

  • ApplicationContext使用描述所有 Bean 的配置元数据创建和初始化。配置元数据可以通过 XML、Java 代码或注释来指定。
  • 对于每个 Bean,其依赖项都以属性、构造函数参数或静态工厂方法的参数的形式表示(如果你使用它而不是普通构造函数)。这些依赖关系在实际创建 Bean 时提供给 Bean。
  • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个 Bean 的引用。
  • 作为值的每个属性或构造函数参数都将从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring可以将以字符串格式提供的值转换为所有内置类型,例如 intlongStringboolean 等。

Spring容器在创建容器时验证每个bean的配置。但是,在实际创建bean之前,不会设置bean属性本身。创建容器时,将创建单例作用域并设置为预实例化(默认)的bean。作用域是在Bean作用域中定义的。否则,仅在请求时创建bean。

循环依赖关系

如果主要使用构造函数注入,则可以创建无法解析的循环依赖关系方案。

例如:类 A 需要通过构造函数注入的类 B 的实例,而类 B 需要通过构造函数注入的类 A 的实例。如果将 Bean 配置为将类 A 和 B 相互注入,则 Spring IoC 容器会在运行时检测到此循环引用,并引发一个 BeanCurrentlyInCreationException

一种可能的解决方案是编辑某些类的源代码,这些类将由 setter 而不是构造函数配置。或者,避免使用构造函数注入,而仅使用 setter 注入。换句话说,尽管不建议这样做,但您可以使用 setter 注入来配置循环依赖关系。

与典型情况(没有循环依赖关系)不同,bean A 和 bean B 之间的循环依赖关系迫使其中一个 bean 在完全初始化自身之前被注入另一个 bean(经典的先有鸡还是先有蛋的场景)。

4.2、依赖关系和配置详情

可直接参考 官方文档 。

4.3、使用 depends-on

通常使用基于XML的配置元数据中的 ref 属性来实现简单的依赖项,然而,有时bean之间的依赖关系不那么直接。例如,需要触发类中的静态初始值设定项,例如数据库驱动程序注册。该属性可以显式强制一个或多个bean在初始化使用该元素的bean之前进行初始化。下面的示例使用 depends-on 属性来表示对单个bean的依赖关系:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

要表达对多个 bean的依赖关系,请提供 bean名称列表作为属性值(逗号、空格和分号是有效的分隔符)

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"><property name="manager" ref="manager" />
</bean><bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

该属性既可以指定初始化时间依赖,也可以指定相应的销毁时间依赖。定义与给定 bean的关系的依赖 bean在给定 bean本身被销毁之前首先被销毁。因此,它还可以控制关闭顺序。

4.4、延迟初始化的 Bean lazy-init

( Lazy-initialized)延迟初始化 bean 告诉 IoC容器在第一次请求时创建bean实例,而不是在启动时创建。在XML中使用 lazy-init="true" 来指定。

<bean id="beanOne" class="ExampleBean" lazy-init="true"/>

4.5、自动装配 autowire

当使用基于XML的配置元数据时,可以使用元素的 autowire 属性为bean定义指定自动装配。

自动装配是Spring满足bean依赖的一种方式!
Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配方式:

  1. 在xml中显式配置
  2. 在java中显式配置
  3. 隐式的自动装配bean(重要)
模式 解释
no (默认值)无自动装配。Bean ref 引用必须由元素定义。对于大型部署,不建议更改默认设置,因为显式指定协作者可提供更好的控制和清晰度。在某种程度上,它记录了系统的结构
byName 按属性名称自动装配。Spring寻找与需要自动装配的属性同名的Bean。例如,如果一个Bean定义被设置为按名称自动装配,并且它包含一个属性master(即,它有一个方法 setMaster(..)),Spring会查找一个名为master 的bean的定义,并使用它来设置属性
byType 如果容器中只存在一个属性类型的 Bean,则允许此自动装配。如果存在多个,则会引发致命异常,这表示您不能对该 Bean 使用byType自动装配。如果没有匹配的 bean,则不会发生任何操作(未设置该属性)
constructor 类似于 byType 但适用于构造函数参数。如果容器中没有构造函数参数类型的一个 Bean,则会引发致命错误

① 手动配置

同包下三个类:人有两个动物

package hom.wang.vo;
import lombok.Data;
@Data
public class People {private Dog dog;private Cat cat;private String name;
}public class Dog {public void say(){System.out.println("汪汪汪");}
}public class Cat {public void say(){System.out.println("喵喵喵");}
}

xml配置:

<bean id="dog" class="hom.wang.vo.Dog"/>
<bean id="cat" class="hom.wang.vo.Cat"/><bean id="people" class="hom.wang.vo.People"><property name="dog" ref="dog"/><property name="cat" ref="cat"/><property name="name" value="小白"/>
</bean>

② 自动装配 autowire="byName"

注意!!!容器内必须存在按照规范命名的 bean的 id(对应setter的属性),否则自动装配失败

xml配置:

<bean id="dog" class="hom.wang.vo.Dog"/>
<bean id="cat" class="hom.wang.vo.Cat"/><bean id="people" class="hom.wang.vo.People" autowire="byName"><property name="name" value="小白"/>
</bean>

③ 自动装配 autowire="byType"

注意!!!容器内必须存在对应类型的 bean的 class(对应setter属性的类型)且全容器此类型bean唯一,否则自动装配失败

xml配置:

<bean id="dog" class="hom.wang.vo.Dog"/>
<bean id="cat" class="hom.wang.vo.Cat"/><bean id="people" class="hom.wang.vo.People" autowire="byType"><property name="name" value="小白"/>
</bean>

自动装配的局限性和缺点:

  • propertyconstructor-arg设置中的显式依赖项始终覆盖自动关联。不能自动关联简单属性,如基本类型、StringsClasses(以及此类简单属性的数组)。此限制是设计使然。

  • 自动装配不如显式装配精确。尽管如前所述,Spring谨慎地避免了猜测,以免出现可能产生意外结果的歧义。Spring托管对象之间的关系不再明确记录。

  • 配线信息可能无法用于从Spring容器生成文档的工具。

  • 容器中的多个Bean 定义可能与要自动连接的setter方法或构造函数参数指定的类型相匹配。对于数组、集合或Map实例,这不一定是问题。然而,对于期望单个值的依赖项,这种模糊性不是任意解决的。如果没有唯一的bean定义可用,则抛出异常。

    • 放弃自动关联,而选择显式关联

    • 通过将 Bean 定义的属性设置为autowire-candidate="false" 来避免自动关联

    • 通过将单个 Bean 定义的元素的primary属性设置为true 来指定其为主要候选项。

    • 实现基于注释的配置可用的更细粒度的控制,如基于注释的容器配置中所述。

从自动装配中排除 Bean:

基于每个 Bean,从自动装配中排除 Bean。将 元素的autowire-candidate属性设置为false。容器使该特定 Bean 定义对自动装配基础结构不可用(包括注释样式配置,如@Autowired)。

该属性设计为仅影响基于类型的自动装配。它不会影响按名称排列的显式引用,即使指定的 Bean 未标记为自动装配候选项,这些引用也会被解析。因此,如果名称匹配,则按名称自动装配仍会注入 Bean。

(提前记录)使用注解实现自动装配

jdk1.5支持的注解,Spring2.5就支持注解了(此小节为提前透点,更多注解相关内容可参阅后续专门开设的注解章节)

使用注解须知:

  1. 导入约束:context约束
  2. 配置注解的支持:<context:annotation-config/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解支持-->  <context:annotation-config/>
</beans>

@Autowired:
直接在属性上使用即可!也可以在set方式上使用!

public class People { @Autowiredprivate Cat cat;@Autowiredprivate Dog dog;/* 也可以在set方式上使用!@Autowiredpublic void setCat(Cat cat) {this.cat = cat;} */
}
  • 默认按属性类型[byType]自动注入

  • 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候 ,可以使用@Qualifier(“xxx”)去配合@Autowired的使用,即既无法通过byName又无法通过byType时,指定一个id的bean对象注入

    public class People {@Autowired@Qualifier("cat222")private Cat cat;
    }
    

    @Resource注解

    @Resource(name=“userDao”, type=UserDao.class) 可以 byType,也可以 byName
    @Resource 默认先 byName,找不到再 byType
    [ 不带属性类似@Autowired,带属性类似@Autowired+@Qualifier组合使用(name和type使用一个即可精准定位) ]

    public class People {@Resource(name="cat1111")private Cat cat;@Resourceprivate Dog dog;
    }
    

    @Autowired和@Resource区别:

  • 都是用来自动装配的,都可以写在字段setter方法上

  • @Autowired 只通过 byType 实现,默认情况下必须要求依赖对象存在

    • 如果要允许null值,可以设置它的required属性false。如果想使用名称装配可以**结合@Qualifier注解**进行使用
  • @Resource 默认通过 byName 方式实现(可以通过name属性进行指定),如果找不到,则通过 byType 实现

    • 需要注意的是,如果name属性一旦指定,就只会按照名称进行装配
  • @Autowired 是Spring提供的注解,@Resource 是JDK提供的注解

4.6、方法注入

可直接参考 官方文档

4、IOC 之Bean的依赖关系相关推荐

  1. Spring IOC学习心得之注册bean的依赖关系

    registerDependentBean方法的解析(注册bean的依赖关系) 源码如下: public void registerDependentBean(String beanName, Str ...

  2. IOC之bean之间的关系讲解

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文主要讲了Spring中Bean之间的关系,分为继承.依赖和引用三个类型.文章中都分别有例子 ...

  3. Spring IoC是如何进行依赖注入的

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 依赖注入(DI) DI(Dependency Injection) ...

  4. Spring核心——Bean的依赖注入

    依赖注入 在设计模式与IoC这篇文章中,介绍了Spring基础的三大支柱的两项内容--IoC.Bean.本篇将继续围绕着Bean的创建时的注入方式来介绍Spring的核心思想与设计模式. 天底下所有面 ...

  5. 依赖注入的三种方式_Spring IoC是如何进行依赖注入的

    依赖注入(DI) DI(Dependency Injection),Spring IoC 不是一种技术,而是一种思想,通过这种思想,能够指导我们设计出松耦合的程序代码.而Spring IoC这个思想的 ...

  6. Spring的IOC创建对象的方式和代码基本解释为什么要有ioc的思维以及Ioc容器和spring依赖注入的解释

    首先我们要知道 Ioc是个啥? ​ IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序.传统应用程序都是由我们在类内部主动创建依赖对象,从而导致 ...

  7. Spring framework(4):IoC (2) Bean 装配

    Spring 装配 Bean 概述 Spring 容器启动的3个条件: Spring 本身:Spring 框架的类包都已经放置在应用程序的类路径下: Bean 配置信息:应用程序为 Spring 提供 ...

  8. Spring IOC和DI之间的关系

    我们通过小故事来理解IOC和DI的关系 现实生活中,比如想吃水饺,在没有水饺店的日子里,最直观的做法就是:买面粉.蔬菜.肉等等食材,再去制造也就是去煮熟.这些都是主动创建的过程.也就是说想吃一顿水饺需 ...

  9. 【spring】jar包详解与模块依赖关系

    以spring3.X为例 jar包详解 1. spring-core.jar:包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心: 2. spri ...

最新文章

  1. IsPostBack须要注意的地方,这些都不是POSTBACK动作
  2. H3C 以太网集线器
  3. idea python工程zip打包_【面试划重点】-- Python常见知识点
  4. Python面向对象,站在更高的角度来思考
  5. Java 身份证工具类
  6. TokenInsight:反映区块链行业整体表现的TI指数较昨日同期下跌1.15%
  7. 【车间调度】基于matlab遗传算法求解置换流水车间调度问题【含Matalb源码 176期】
  8. 深入浅出通信原理 陈爱军——读书笔记1
  9. win10专业版激活工具很不错!
  10. exlsx表格教程_excel表格制作教程
  11. LIBSAS/SAS驱动代码分析(1)之概述
  12. 27岁了,程序员写给自己的一封信
  13. 前端之HTML学习笔记一(B站黑马程序员)
  14. 买天猫网店转让商标需要注意的
  15. 深信服2018校园招聘C++工程师编程题 - 题解
  16. bim的二次开发需要什么语言_CAD二次开发语言简介
  17. Hive动态分区和分桶
  18. auto.js B0021 图片查找 订阅功能 2021-10-07
  19. 《缠中说禅108课》21:缠中说禅买卖点分析的完备性
  20. 值得珍藏一生的经典电影台词

热门文章

  1. 枝枝叶梗高傲的顶着莲蓬像是
  2. 计算机音乐对演出的用处,音乐对想象力的影响作用
  3. 四柱八字大全 php,四柱八字查询表 免费四柱八字查询
  4. 计算机屏幕的作用是什么,电脑屏幕分辨率是什么?有什么作用
  5. SpringBoot word文档转pdf
  6. 【图像加密】基于matlab GUI正交拉丁方置乱+混沌图像加密解密【含Matlab源码 636期】
  7. 有人说:低代码接力中台燃起燎原之火,低代码的火是如何起来的?
  8. ACM解题的一些技巧和方法
  9. 剑指 Offer 06. 从尾到头打印链表
  10. 1个月写900多条用例,二线城市年薪33W+的测试经理能有多卷?