Spring 依赖注入详解

这篇具有很好参考价值的文章主要介绍了Spring 依赖注入详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、基于构造器的依赖注入

2、基于 Setter 方法的依赖注入

3、使用构造器注入还是 setter 方法注入?

4、依赖注入解析的过程

5、依赖注入的相关示例


// 依赖关系,指的就是对象之间的相互协作关系

        依赖注入(DI)是一个过程,在这个过程中,对象仅通过构造函数参数、工厂方法的参数或在对象被实例化后通过属性设置来定义它们的依赖项(即与该对象一起工作的其他对象)。然后,容器在创建 bean 时注入这些依赖项。这个过程基本上是与对象直接通过构造类或等机制来控制其依赖项的实例化或位置是相反的,因此得名控制反转。// 对象不直接创建自己,而是通过 Spring 容器创建,那么 Spring 容器是如何创建对象的?

        使用 DI 原则,代码会更简洁,当对象具有依赖关系时,也能更有效的解耦。对象不检索它的依赖项,也不知道依赖项的具体类或者位置。因此,你的类将变得更容易测试,特别是当依赖关系建立在接口或者抽象基类上时,他们的子类或者模拟实现将允许在单元测试中被使用。// 依赖注入最大的好处,就是代码可以解耦合变得更加简洁

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

1、基于构造器的依赖注入

        基于构造函数的 DI 是通过容器调用构造函数来实现的,如果构造函数带有参数,那么每个参数都表示一个依赖项。通过特定参数调用静态工厂方法去构造 bean 也是一样的,这两种方式并没有什么本质上的不同。下面例子展示了一个只通过构造函数来进行依赖注入的类:

public class SimpleMovieLister {

    // SimpleMovieLister依赖于MovieFinder
    private final MovieFinder movieFinder;

    // 构造函数,Spring容器可以注入MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) { 
        this.movieFinder = movieFinder;
    }

    // 实际使用 MovieFinder 对象的商业逻辑被省略...
}

        这个类它是只一个不依赖容器中特定接口、基类或注释的普通 POJO 类,并无特别之处。

构造函数的参数解析

        构造函数的参数解析是通过匹配参数的类型进行的。如果在 bean 定义中的构造函数参数不存在歧义,那么在 bean 定义中的参数顺序就是在实例化 bean 时向构造函数提供的参数的顺序。例如下边这个类:// 通过匹配的参数类型进行构造函数解析

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

        假设 ThingTwo 和 ThingThree 类之间没有继承关系,不存在潜在的歧义。那么,下边的配置将会正常工作。在该配置中,你不需要在 <constructor-arg/> 标签中去显式指定构造函数参数的索引或类型。// 不用显示的指定参数的索引或者类型

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

        当引用一个 bean 时,它的类型是已知的,可以通过类型进行匹配。但是,当使用简单类型时,例如 <value> true </value>, Spring 就无法确定值的类型,所以,Spring 在没有帮助的情况下无法按类型进行匹配。例如以下类: // 可以通过参数类型、参数位置、参数名字进行匹配

package examples;

public class ExampleBean {

    // 计算最终答案的年数
    private final int years;

    // 最终的答案:生命、宇宙和一切的答案
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

(1)构造函数实参的类型匹配

        在上述场景中,如果你使用 type 属性显式指定构造函数的参数类型,那么容器就可以使用简单类的类型进行匹配,如下例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

(2)构造函数参数的索引匹配

        你也可以使用 index 属性显式指定构造函数参数的索引位置,如下例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

        除了解决多个简单值的模糊性外,指定索引还解决了构造函数具有两个相同类型参数时的模糊性。

(3)构造函数参数的名字匹配

        你也可以使用构造函数的参数名字来消除值的歧义,如下面的例子所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

        请记住,如果要想开箱即用,必须在编译代码时启用调试标志,这样 Spring 才能从构造函数中查找到参数名。如果不能或不希望用调试标志编译代码,可以使用 JDK 中的 @ConstructorProperties 注释显式的命名构造函数参数。示例如下所示:// 这样的写法还真是罕见,不理解,不过不影响继续阅读

package examples;

public class ExampleBean {

    // 省略全局属性...

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

2、基于 Setter 方法的依赖注入

        基于 setter 方法的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法实例化 bean 之后,再调用 bean 上的 setter 方法来实现的。// 其实就是实例化后的属性赋值,需要无参构造函数

        下面示例展示了一个通过 setter 方法进行依赖注入的类:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // 实际使用注入的 MovieFinder 的业务逻辑被省略了...
}

        ApplicationContext 支持基于构造函数和基于 setter 方法的依赖注入。当一个 Bean 通过构造函数注入了一些依赖项后,还可以通过 setter 方法进行属性赋值。你可以通过 BeanDefinition 的形式配置一个 Bean 依赖项,将其与 PropertyEditor 实例一起使用,能够将属性从一种格式转换为另一种格式。然而,大多数 Spring 用户并不直接使用这些类,而是使用 XML 配置、Spring 注解(例如,使用 @Component、@Controller 等注释的类),或者 Java 代码配置(在 @Configuration 标记的类中使用 @Bean 注解的方法)。然后,在内部将这些配置转化为 BeanDefinition,然后将创建的实例加载到 Spring 容器中。// 最常见的方法还是通过配置文件进行配置。

3、使用构造器注入还是 setter 方法注入?

        构造器注入和 setter 方法注入两者可以混合使用,对于强制依赖项推荐使用构造器注入,对于可选依赖项则推荐使用 setter 方法注入,这是一个非常好的经验规则。虽然,在 setter 方法上使用 @Required 注释也可以使属性成为必需依赖项;但是,通过构造器来注入必须依赖项显然更加的合适。// 强制依赖使用构造器注入,可选依赖使用 setter 方法注入

        Spring 官方推荐使用构造器注入,通过构造器注入会将应用程序中的对象实现为不可变对象,并确保对象的必须属性不为空。而且,通过构造器注入返回的实例总是完全初始化后的对象(非半成品)。当然,构造器拥有太多参数是一种不好的代码设计,意味着该构造类负责的功能太多,需要进行代码重构,以便于使类的职责更加单一。// 构造器注入的优势

        Setter 方法主要应用于可选的依赖项注入,同时,这些依赖项需要在类中分配合理的默认值。如果在不分配默认值的情况下,那么需要在代码使用该依赖项的所有地方都执行非空检查。使用 Setter 方法注入的好处是,该方法可以使对象在之后进行重新配置或重新注入。因此,通过  JMX MBeans 进行对 setter 方法注入进行管理是一种非常不错的方式。

        根据不同的类去选择不同的注入方式。有时,在处理没有源代码的第三方类时,你需要做出选择。例如,如果第三方类不公开任何 setter 方法,那么构造器注入是唯一可选用的注入方式。

4、依赖注入解析的过程

容器执行 bean 依赖解析的过程如下:

  • 通过配置文件配置的 bean 元数据创建并初始化 ApplicationContext,元数据配置格式可以是 XML、Java代码或 Spring 注解。
  • 对于每个 bean,它的依赖关系都是通过属性、构造器参数或静态工厂方法参数的形式表示(如果创建实例使用静态工厂方法)。当实际创建 bean 时,将向 bean 提供这些依赖项。
  • 每一个属性或者构造器参数都需要去设置一个实际的值,或者引用容器中的另一个 bean。
  • 设置的值会根据指定的格式,转化为该属性或者构造器参数的实际类型。默认情况下,Spring 可以将字符串格式的值转换为所有的内置类型,比如 int,long,String,boolean 等等。

        当前容器创建的时候,Spring 容器会校验每个 bean 的配置。但是,Spring 容器在实际创建 bean 之前,并不会去设置 bean 的属性。当容器创建的时候,只有单例 Bean 会被提前创建(pre-instantiated),其他域(Scopes)的 Bean 只有被访问到时才会去创建。创建一个 Bean 的时候,该 Bean 的依赖项,以及依赖项的依赖项都会被创建和分配。这种解析机制,会导致当创建一个 Bean 的依赖项不匹配时,可能需要经过一系列的解析之后才能被发现。// 依赖注入过程中,了解哪些 bean 将会被创建

循环依赖问题

        如果使用构造器进行注入,就有可能出现循环依赖的情况。

        例如:class A 通过构造函数注入,需要 class B 的实例,class B 通过构造函数注入,需要 class A 的实例。如果将 class A 和 class B 的 bean 定义配置为相互注入,那么 Spring IoC 容器会在运行时检测到这个循环引用,并抛出 BeanCurrentlyInCreationException 异常。// 循环依赖场景

        一种可行的解决方法是修改其中一些类的源代码,把构造器注入改成 setter 方法注入,或者涉及到循环依赖的所有类都使用 setter 方法注入,尽管这样做不推荐,但是使用 setter 方法注入可以有效的解决循环依赖的问题// 因为 setter 方法是在对象实例化后才设置属性的

        与普通的情况相比,循环依赖关系要求 bean A 或 bean B 在注入之前就已经被完全的实例化(典型的是先有鸡还是先有蛋的场景)。

        Spring 会在容器加载时检测配置问题,例如,引用的 bean 不存在,bean 之间存在循环依赖等。当实际创建 bean 的时候,Spring 会尽可能晚的去设置 bean 的属性和解析依赖项。这意味着,如果创建 bean 或 bean 的依赖项时出现了问题,Spring 容器可以在用到该 bean 的时侯再抛出异常。但是,这样做可能导致配置问题不能及时被发现,这也是为什么 ApplicationContext 在创建容器时,需要把 bean 提前实例化(pre-instantiated)的原因。默认情况下,在创建  ApplicationContext 容器时就可以发现 bean 的配置问题,不过这样做,需要耗费一些时间和内存去创建一些不被马上用到的 bean。当然,你也可以替换这个默认行为,让一些 bean 实现懒加载而不是过早的提前实例化。// 提前实例化可以及时发现配置问题,这一段很重要

        在没有循环依赖的情况下,将一个或多个 bean 注入到另一个 bean 中时,每个 bean 在注入之前就已经被完全实例化了。也就是说,如果创建 bean A 需要注入 bean B,那么 Spring 容器会在调用 bean A 的 setter 方法之前,就已经完全实例化了 bean B。一个 bean 被完全实例化,意味着它的依赖关系已经确定,相关的生命周期方法也已经被调用(例如配置的 init 方法或 InitializingBean 回调方法等)。//  完全被实例化的 bean,属性和依赖关系都已经被确定好了

5、依赖注入的相关示例

        使用 xml 配置的 setter 方法注入,在 xml 配置文件中指定了一些 bean 定义,如下所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- 使用嵌套的ref标签进行Setter注入 -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- 使用更整洁的ref属性进行Setter注入 -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

        下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }
    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }
    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

        在上边的示例中,代码声明的 setter 方法与 xml 文件配置的属性一一匹配。下面的例子使用了基于构造器的依赖注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- 使用嵌套ref元素的构造函数注入 -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- 使用更整洁ref属性的构造函数注入 -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

        下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

        现在考虑另一个情况,Spring 不是使用构造函数,而是调用一个静态工厂方法来返回对象的实例:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

        下面的示例显示了相应的 ExampleBean 类:

public class ExampleBean {

    // 私有构造函数
    private ExampleBean(...) {
        ...
    }

    // 静态工厂方法; 
    // 方法的参数可以被认为是返回bean的依赖项,而不用它们是怎么被使用的.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}

        工厂方法的参数由 <constructor-arg/> 标签提供,就像实际使用了构造函数一样。由工厂方法返回的 bean 的类型不必与原类的类型相同(尽管在本例中它是相同的)。非静态的实例工厂方法跟静态的工厂方法使用的方式基本相同,所以不再详细描述。文章来源地址https://www.toymoban.com/news/detail-460844.html

到了这里,关于Spring 依赖注入详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring 依赖注入详解

    目录 1、基于构造器的依赖注入 2、基于 Setter 方法的依赖注入 3、使用构造器注入还是 setter 方法注入? 4、依赖注入解析的过程 5、依赖注入的相关示例 // 依赖关系,指的就是对象之间的相互协作关系         依赖注入(DI)是一个过程,在这个过程中,对象仅通过构造函

    2024年02月06日
    浏览(46)
  • 【Spring进阶系列丨第五篇】详解Spring中的依赖注入

    全称 Dependency Injection(DI) 与IoC的关系 IoC和DI其实说的是一个意思,可以这么说: IoC是一种思想,DI是对这种思想的一种具体实现 依赖关系的管理 以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。 依赖关系的维护

    2024年02月04日
    浏览(54)
  • spring(1):基于XML获取Bean对象以及各种依赖注入方式

    1.1 根据id获取 1.2 根据类型获取 1.3 根据id和类型获取 注意: 当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个。 例如以下XML文件,当IOC容器中一共配置了两个,根据类型获取时会抛出异常。 根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:

    2024年01月25日
    浏览(44)
  • Springboot依赖注入Bean的三种方式,final+构造器注入Bean

    @Autowired注解的一大使用场景就是Field Injection。 通过Java的反射机制实现,所以private的成员也可以被注入具体的对象 优点 代码少,简洁明了。 新增依赖十分方便,不需要修改原有代码 缺点 容易出现空指针异常。Field 注入允许构建对象实例时依赖的对象为空,导致空指针异常

    2024年02月02日
    浏览(48)
  • VS依赖注入(DI)构造函数自动生成局部私有变量

    依赖注入(DI)在开发中既是常见的也是必需的技术。它帮助我们优化了代码结构,使得应用更加灵活、易于扩展,同时也降低了各个模块之间的耦合度,更容易进行单元测试,提高了编码效率和质量。我们经常会先定义局部变量,再在构造函数中使用,每次都要这样去编写耗时

    2024年02月11日
    浏览(45)
  • 【Spring教程十】Spring框架实战:全面深入详解IOC/DI之--纯注解开发模式下的依赖注入&&注解读取properties配置文件

    欢迎大家回到《 Java教程之Spring30天快速入门》,本教程所有示例均基于Maven实现,如果您对Maven还很陌生,请移步本人的博文《 如何在windows11下安装Maven并配置以及 IDEA配置Maven环境》,本文的上一篇为《 全面深入详解IOC/DI注解开发》 Spring为了使用注解简化开发,并没有提供

    2024年02月04日
    浏览(54)
  • Spring DI简介及依赖注入方式和依赖注入类型

    目录 一、什么是依赖注入 二、依赖注入方式 1. Setter注入 2. 构造方法注入 3. 自动注入  三、依赖注入类型 1. 注入bean类型 2. 注入基本数据类型 3. 注入List集合 4. 注入Set集合 5. 注入Map集合 6. 注入Properties对象 往期专栏文章相关导读  1. Maven系列专栏文章 2. Mybatis系列专栏文章

    2024年02月02日
    浏览(46)
  • spring——依赖注入原理及注入方式

    🟣1.依赖注入(Dependency Injection,DI) 是一种设计模式和编程技术,其原理是将对象的依赖关系由外部容器来管理和注入。它的目的是解耦组件之间的依赖关系,提高代码的灵活性、可维护性和可测试性。 🟣2.依赖注入的原理 是通过在对象的构造函数、属性或方法中注入所依

    2024年02月08日
    浏览(40)
  • 【Spring】浅谈spring为什么推荐使用构造器注入

    因本人实力有限,该文章主要内容(在文章基础上加了点点东西)均来自: 原文链接:https://www.cnblogs.com/joemsu/p/7688307.html 作者:joemsu ​ Spring框架对Java开发的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反转)和AOP,平时使用最多的就是其中的IOC,我们通过

    2024年02月13日
    浏览(45)
  • Spring面试整理-Spring的依赖注入

    Spring框架的依赖注入(DI)是其核心功能之一,它允许对象定义它们依赖的其他对象,而不是自己创建或查找它们。这种机制促进了松耦合和更容易的测试。 依赖注入是一种设计模式,其中一个对象或方法提供另一个对象的依赖关系。在Spring中,这些依赖通常是服务、配置值

    2024年01月19日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包