Spring中的依赖关系(DI) 您所在的位置:网站首页 spring实现di的方式 Spring中的依赖关系(DI)

Spring中的依赖关系(DI)

2023-03-25 20:11| 来源: 网络整理| 查看: 265

        前言: 最近没事在浏览Spring官网,简单写一些相关的笔记,这篇文章整理Spring依赖注入。并不包含依赖注入所有技术点,只是记录有收获的内容

        最简单的应用程序也有多个对象, 他们一起工作来表示用户所看到的应用程序。依赖注入(Dependency injection即 DI)是一个过程, 在这个过程中,对象通过构造函数的参数、工厂方法的参数或在对象实例后通过设置的属性来定义其依赖项。然后容器在创建bean时注入这些依赖项。

1.什么是依赖入住 DI

        Spring 以“依赖注入”的方式实现了“控制反转"的效果,以便符合软件工程中“依赖倒置”原则。使用依赖注入(Dependency injection即 DI)原则,程序代码更干净,当对象有依赖关系时,解耦更有效。对象不查找其依赖项,也不知道依赖项的位置。测试类变得更容易,特别是当依赖项在接口或抽象基类上时

1.1.基于构造函数

依赖注入(DI)实现一般通过两种方式:基于构造函数的依赖注入和基于Setter的依赖注入, 基于构造函数的DI是通过容器调用一个具有多个参数的构造函数来完成的,每个参数表示一个依赖项。以下示例显示了通过构造函数注入依赖的类:

package x.y; public class ThingOne { public ThingOne(ThingTwo thingTwo, ThingThree thingThree) { // ... } }

bean定义中定义构造函数“参数的顺序”就是实例化bean时将这些参数提供给相应构造函数的顺序。假设ThingTwo和ThingThree类没有继承关系,以下配置工作正常时,不需要在元素中显式指定构造函数参数索引或类型。

当引用另一个bean时,类型是已知的,并且可以进行匹配,当使用简单类型时,例如true,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; } }

此时可以使用type属性显式指定构造函数参数的类型

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

索引除了解决多个简单值的模糊性之外,如果构造函数有两个相同类型的参数,则指定索引还可以解决模糊性。但是一定要注意索引是从序号0开始

当然还可以使用构造函数参数名称消除值歧义,如下例所示

1.2.基于Setter

基于Setter的DI是在调用无参数构造函数或无参数静态工厂方法来实例化bean之后,通过容器在bean上调用Setter方法来实现的.例如下面传统的java类

public class SimpleMovieLister { private MovieFinder movieFinder; public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }

1.3 选择基于构造&&基于Setter

Spring团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化状态返回给客户端(调用)代码。作为补充说明,大量构造函数参数是一种糟糕的代码味道,这意味着类可能有太多的责任,应该进行重构以更好地解决关注点的适当分离

Setter注入应该主要用于可在类中分配合理默认值的可选依赖项。否则,在代码使用依赖项的任何地方都必须执行非空检查。setter注入的一个好处是,setter方法使该类的对象能够在以后重新配置或重新注入。因此,通过JMX MBean进行管理是setter注入的一个引人注目的用例

2.依赖注入详细配置 2.1依赖其他的bean

以下示例是基于setter的DI

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; } }

 以下示例使用基于构造函数的DI

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; } public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } }

现在考虑这个示例,其中Spring被告知调用静态工厂方法以返回对象的实例,而不是使用构造函数:静态工厂方法的参数由元素提供,与实际使用构造函数时完全相同

2.2配置直接获取的值(Straight Values )

2.3集合配置(Collection)

、、和元素分别设置Java集合类型list、set、map和properties的属性和参数

[email protected] [email protected] [email protected] a list element followed by a reference just some string

2.4空字符串和Null

Spring将属性的空参数等视为空字符串。以下基于XML的配置元数据片段将电子邮件属性设置为空String值

元素处理空值 

2.5属性嵌套

设置bean属性时,可以使用复合或嵌套属性名,只要路径的所有组件(最终属性名除外)都不为空

2.6强制依赖depends-on

如果某个bean是另一个bean的依赖项,这通常意味着将一个beam设置为另一个bean的属性。通常,您可以使用基于XML的配置元数据中的元素来实现这一点。有时bean之间的依赖关系不那么直接. depends-on属性可以显式地强制一个或多个bean在初始化使用此元素的bean之前进行初始化

例如beanOne强制依赖manager和accountDao

2.7懒加载Bean

默认情况下,ApplicationContext实现急切地创建和配置所有单例bean,作为初始化过程的一部分。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是几小时甚至几天之后。当这种行为不可取时,可以通过将bean定义标记为惰性初始化来防止单例bean的预实例化。惰性初始化bean告诉IoC容器在第一次请求bean时创建bean实例,而不是在启动时创建

2.8XML快捷p-namespace

Spring支持具有名称空间的可扩展配置格式,p-名称空间允许您使用bean元素的属性来描述协作bean的属性值

下面的例显示了bean定义中名为email的p命名空间中的属性。这告诉Spring包含一个属性声明。p命名空间没有模式定义,因此可以将属性的名称设置为属性名称。

2.9XML快捷c-namespace

和具有p名称空间的XML快捷方式类似,Spring3.1中引入的c名称空间允许内联属性来配置构造函数参数,而不是嵌套构造函数参数元素

3.常见问题 3.1 循环依赖

Spring容器在创建容器时验证每个bean的配置。在实际创建bean之前,不会设置bean属性。在创建容器时,单例作用域的bean会被预实例化,而其他的bean只有在请求时才会创建bean。需要注意的是,如果不存在循环依赖项,当一个或多个协作bean被注入到依赖bean中时,每个协作bean在注入到依赖bean之前都会被完全配置。这意味着,如果bean A依赖于bean B,那么Spring IoC容器会在调用bean A上的setter方法之前完全配置bean B

使用构造器注入时可能会创建无法解析的循环依赖场景。bean A和bean B之间的循环依赖关系强制一个bean在被完全初始化之前被注入到另一个bean中,这就是一个典型的先有鸡还是先有蛋的问题。

例如:类A通过构造函数注入需要类B的实例,类B通过构造函数注入要求类A的实例。如果将类A和B的bean配置为相互注入,Spring IoC容器将在运行时检测此循环引用,并抛出BeanCurrentlyInCreateException。

一种解决方案是编辑某些类的源代码,以便由setter而不是构造函数进行配置。或者避免构造函数注入,只使用setter注入。

3.2 属性缺失

Spring在实际创建bean时尽可能晚地设置属性并解析依赖项。这意味着,如果在创建对象或其依赖项时出现问题,正确加载的Spring容器稍后可以在您请求对象时生成异常 — 例如,bean由于缺少属性或属性无效而引发异常

上一篇:Spring中的作用域Bean Scope



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有