本文将介绍在使用Spring框架时可能使用的依赖注入类型,包括构造器注入、Setter注入、字段注入、查找方法注入等。如果想要了解它们的区别,可以继续阅读本文,详细了解各种方法的优缺点。但需要提醒的是,本文较长,需要耐心阅读。
声明
对于每种依赖注入类型,我们将提供纯XML配置的示例(如果可能),以展示其思想,然后再提供XML +注释配置的示例,以展示更简单、更常见的依赖项配置方式。为了简洁起见,我们不会展示Java配置示例。
构造器注入 - 强制不可变性
这是依赖注入最直接、最推荐的方式。一个依赖类具有一个构造函数,在其中设置所有的依赖项,它们将由Spring容器根据XML、Java或基于注释的配置提供。
让我们从一个例子开始:
我们有两个独立的服务和一个依赖服务。依赖关系仅在构造函数中设置,要运行它,我们初始化容器,并提供包含在spring.xml中的配置。现在让我们看看配置。
因此,我们刚刚声明了我们的三个服务作为Spring beans,最后一个引用了前两个作为构造函数参数。但是,这看起来相当冗长,是否需要编写这么多的配置?幸运的是,我们可以使用“自动装配”——让Spring猜测应该在哪里注入哪些内容,我们只需要进行最少的配置工作即可。为了帮助Spring定位我们的代码,让我们使用@Service注释标记我们的服务,使用**@Autowired或@Inject**注释标记进行注入的构造函数。@Autowired属于Spring框架,@Inject属于JSR-330注释集合,两者都适用于我们的例子。
好了,完成了。但是我们需要在spring.xml中改变一些东西吗?肯定要!
我们刚刚指定了我们的服务将在哪个包中,是不是很简单?但与纯XML方式相比,这种新的自动装配方法有一些缺点。在XML配置中,我们引用了确切的应该注入的bean,在新的自动装配方法中,Spring会在上下文中查找匹配的bean,并且有时需要我们的帮助。例如,有两个实现一个接口的类(bean),我们想在代码的某个地方使用该接口进行自动装配,在Spring中,我们应该使用@Qualifier注释并标识我们所选择的实现的标识符,以使其在这种特定情况下有效。
Spring提供了在纯XML方法中使用自动装配的可能性。但是,强烈建议避免使用此功能。
重要的一点是,你的类中可以有更多的构造函数,但只有一个构造函数应该符合依赖注入。
构造器注入的优点
- 构建的对象是不可变的,并以完全初始化的状态返回给客户端。
- 这种方法可以立即显示与增加依赖项有关的问题。依赖项越多,构造函数越大。
- 可以与Setter注入或字段注入相结合,构造函数参数表示所需的依赖项,其他参数是可选的。
缺点
- 没有更改对象依赖项的可能性——不灵活。
- 有更高的循环依赖的风险,所谓的鸡生蛋的情况。
Setter注入 - 支持可变性
为了展示这种注入方式,我将使用之前示例中的相同代码。由于Setter注入与构造器注入相比没有太大变化,为了简洁起见,只展示已更改的逻辑部分。
正如注入类型的名称所示,我们首先应该为我们的依赖项创建Setter。在这种情况下,不需要构造函数。让我们看看XML配置中应该做些什么。
所以,这很简单,不是吗?为了更好地展示和感受,让我演示一下基于注释的版本。
我们只需在Setter上放置@Autowired注释即可。同样,@Inject也可以在此处正常工作,没有任何问题。
在Setter注入中,需要存在Setter,就像在构造器注入中需要存在构造器一样。
在描述构造器注入的优点时,我提到它可以很容易地与Setter注入相结合。现在是展示它的时候了。
在我们的示例中,service1对象是一个强制依赖项(最终属性),只在实例化DependentService时设置一次。与此同时,service2是可选的,一开始可以为空,其值可以通过调用Setter在创建后随时更改。在XML方式中,你必须在bean声明中指定property和constructor-arg标签。
优点
- 解决了构造器注入的循环依赖问题,具有依赖项解析或对象重新配置的灵活性。可以随时更改依赖项。
- 可以与构造器注入相结合,构造函数参数表示所需的依赖项,其他参数是可选的。
缺点
- 需要进行空检查,因为依赖项可能尚未设置。
- 可能比构造器注入更容易出错,更不安全,因为它可能会覆盖依赖项。# 字段注入 - 没有人喜欢它?
这种类型的注入只在基于注解的方法中可用,因为它实际上不是一种新的注入类型 - 在底层,Spring使用反射来设置这些值。
为什么我要单独提到这种方法?有一次有人问我 - 你在项目中使用哪种注入类型,setter还是constructor?然后我看了看代码,只看到了字段上的注解,没有setter,也没有constructor。我承认我感到困惑。为什么这么少提到它,即使提到也会说一些像“不要使用它,没有办法”的话?让我们讨论一下这种方法的优点和缺点。
优点
- 使用简单,不需要构造函数或setter
- 可以很容易地与构造函数和/或setter方法相结合使用
缺点
- 对对象实例化的控制较少。为了实例化类的对象进行测试,你需要配置一个Spring容器或模拟库 - 取决于你正在编写的测试。
- 依赖关系的数量可能会达到几十个,直到你注意到设计上出了问题。
- 没有不可变性 - 与setter注入相同。
查找方法注入 - 什么?为什么?何时使用?
由于特殊用例,即注入具有更短生命周期的依赖项,此注入类型比上述所有其他注入类型都要少使用。让我简要解释一下。
默认情况下,Spring中的所有bean都是单例,这意味着它们将在容器中创建一次,并且相同的对象将在请求任何地方注入。但是,有时需要不同的策略,例如,每个方法调用应该使用新对象完成。现在想象一下,这个短寿命对象被注入到单例对象中,Spring会自动在每次调用时刷新此依赖项吗?不会,除非我们指示这种特殊依赖项的存在。
回到实践中,我们再次有了3个服务,其中一个依赖于其他服务,service2是通常可以通过先前描述的任何依赖项注入技术(例如setter注入)注入到DependentService中的对象。 Service1的对象将是不同的,它不能一次性注入,每次调用都应该访问一个新实例 - 让我们创建一个将提供此对象并让Spring知道它的方法。
我们没有将Service1的对象声明为常规依赖项,而是指定了一个方法,Spring框架将覆盖该方法以返回Service1类的最新实例。
依赖项的方法提供程序不必是抽象和受保护的,它可以是公共的并包含实现,但请记住,它将被Spring创建的子类覆盖。
我们再次看一个纯XML配置示例,因此我们首先必须指示service1是具有短生命周期的对象,在Spring术语中,我们可以在这里使用原型范围,因为它比单例范围小。通过lookup-method标记,我们可以指定将注入依赖项的方法的名称。
可以以基于注解的方式完成相同的操作。
缺点和优点
与其他类型的依赖注入进行比较是不正确的,因为它具有完全不同的用例。
结论
我们介绍了Spring框架实现的4种依赖注入类型:
- 构造函数注入 - 好的,可靠的和不可变的,通过构造函数之一进行注入。可以在:XML,XML +注释,Java,Java +注释中配置。
- Setter注入 - 更灵活,可变的对象,通过setter进行注入。可以在:XML,XML +注释,Java,Java +注释中配置。
- 字段注入 - 快速,方便,与IoC容器耦合。可以在XML +注释,Java +注释中配置。
- 查找方法注入 - 与其他完全不同,用于注入范围较小的依赖项。可以在:XML,XML +注释,Java,Java +注释中配置。
尽管构造函数倾向于是推荐的方法,但我建议你首先澄清你的需求,也许setter或字段注入更适合你的应用程序。无论如何,你始终可以混合不同的方法并实现你的目标。
译自:https://medium.com/@ilyailin7777/all-dependency-injection-types-spring-336da7baf51b
评论(0)