解释为什么构造函数注入比其他选项更好



在Pro Spring 3书中,第4章 - 春季的IOC和DI简介 - 第59页,在"二式注射器注入构造者注入"部分中,一段说

包括春季,提供了一种机制,以确保定义所有依赖关系 您使用二传剂注入,但是通过使用构造函数注入,您可以以容器 - 敏锐的方式主张依赖性的要求"

"

您可以用示例解释

一个将所需依赖性作为构造函数参数的类仅在提供该参数的情况下才能实例化(您应该拥有一个守卫条款,以确保参数不是null)(或在Kotlin中使用不可删除的类型)。因此,构造函数会执行依赖关系要求

如果您使用设置器注入,则可以或可能不会调用设置器,因此永远不能为实例提供其依赖关系。强迫固定器被调用的唯一方法是使用@Required@Autowired,这是特定于弹簧的,因此不是容器 - 敏捷。

因此,要使您的代码独立于弹簧,请使用构造方参数进行注入。这适用于测试;您将有更轻松的时间在普通单元测试中进行实例化和测试,而无需配置应用程序上下文或设置集成测试带来的复杂性。

UPDATE :Spring 4.3将在单构建器方案中执行隐式注入,从而使您的代码完全不需要@Autowired批评,从而使您的代码更加独立于Spring。

(...)通过使用构造函数注入,您以容器 - 敏锐的方式主张依赖关系的要求

这意味着您可以在不使用任何容器特定解决方案的情况下对所有注射字段执行要求。


设置器注入示例

使用设置器注入特殊弹簧注释@Required

@required

标记一种方法(通常是Javabean Setter方法)为'必需':也就是说,必须将setter方法配置为注入值。

用法

import org.springframework.beans.factory.annotation.Required;
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class Foo {
    private Bar bar;
    @Inject
    @Required
    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

构造函数注入示例

所有必需的字段均在构造器,纯Java解决方案中定义。

用法

import javax.inject.Inject;
import javax.inject.Named;
@Named
public class Foo {
    private Bar bar;
    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }
}

单元测试

这在单位测试中特别有用。这种测试应该非常简单,并且不理解诸如@Required之类的注释,通常不需要弹簧即可进行简单的单元测试。使用构造函数时,该类的设置进行测试要容易得多,无需分析如何实现测试类别。

简单,让我们说,我们可以将基于构造函数的依赖注入用于可选依赖性的强制依赖性和基于二传剂的注入。这是一个经验法则!!

例如,假设。

如果您想实例化课程,则始终使用其构造函数进行。因此,如果您使用基于构造函数的注入,那么实例化类的唯一方法是通过该构造函数。如果您通过构造函数将依赖关系传递,则很明显它是强制性的依赖。

另一方面,如果您在POJO类中有一个设置器方法,则可以使用该设置器方法为您的类变量设置值或可能不会设置值。它完全基于您的需求。即是可选的。因此,如果您通过类的设置方法传递依赖项,则隐含地意味着它是可选的依赖关系。希望这很清楚!

当同类没有依赖类的情况下,使用构造函数注入。

当类可以在没有依赖类的情况下可以运行时使用属性注入。

作为一个具体的例子,请考虑一个依赖于Iservice进行工作的服务。由于没有Iservice,ServicerePositor无法有效地运行,因此通过构造函数注入它是有意义的。

相同的ServicerePosority类可以使用记录器进行跟踪。可以通过注入财产注射来注入ilogger。

其他常见的财产注入示例是ICACHE(AOP术语中的另一个方面)或Ibaseproperty(基类中的属性)。

通过使用构造函数注入,您可以以容器 - 敏捷的方式主张依赖关系的要求

我们需要来自IOC容器的保证,在使用任何豆子之前,必须进行必要的豆类。

setter注入策略中,我们相信IOC容器将首先创建bean,但在使用bean使用setter方法之前将立即进行注入。并且注射是根据您的配置完成的。如果您以某种方式错过了指定在配置中注入的任何豆子,则不会为这些豆类进行注入,并且您的依赖性豆在使用时不会相应地运行!

但在构造器注入策略中,容器强加(或必须强加)在构造Bean时正确提供依赖项。这被称为"容器 - 敏捷的方式" ,因为我们在创建bean时需要提供依赖关系,从而使依赖关系的可见性与任何IOC容器无关。

编辑:

Q1: 以及如何防止使用null值的构造函数来创建bean而不是缺少bean?

您没有选择真正错过任何<constructor-arg>(如果在春季),因为IOC容器强加了您以提供匹配提供的创建BEAN所需的构造函数所需的所有构造函数。如果您有意在<constructor-arg>中提供null。然后,IOC容器无法使用它!

控制器类:

@RestController
@RequestMapping("/abc/dev")
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class MyController {
//Setter Injection
@Resource(name="configBlack")
public void setColor(Color c) {
    System.out.println("Injecting setter");
    this.blackColor = c;
}
public Color getColor() {
    return this.blackColor;
}
public MyController() {
    super();
}
Color nred;
Color nblack;
//Constructor injection
@Autowired
public MyController(@Qualifier("constBlack")Color b, @Qualifier("constRed")Color r) {
    this.nred = r;
    this.nblack = b;
}
private Color blackColor;
//Field injection
@Autowired
private Color black;
//Field injection
@Resource(name="configRed")
private Color red;
@RequestMapping(value = "/customers", produces = { "application/text" }, method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.CREATED)
public String createCustomer() {
    System.out.println("Field injection red: " + red.getName());
    System.out.println("Field injection: " + black.getName());
    System.out.println("Setter injection black: " + blackColor.getName());
    System.out.println("Constructor inject nred: " + nred.getName());
    System.out.println("Constructor inject nblack: " + nblack.getName());

    MyController mc = new MyController();
    mc.setColor(new Red("No injection red"));
    System.out.println("No injection : " + mc.getColor().getName());
    return "Hello";
}
}

接口颜色:

public interface Color {
    public String getName();
}

类红色:

@Component
public class Red implements Color{
private String name;
@Override
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public Red(String name) {
    System.out.println("Red color: "+ name);
    this.name = name;
}
public Red() {
    System.out.println("Red color default constructor");
}
}

类黑色:

@Component
public class Black implements Color{
private String name;
@Override
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public Black(String name) {
    System.out.println("Black color: "+ name);
    this.name = name;
}
public Black() {
    System.out.println("Black color default constructor");
}
}

为创建bean的配置类:

@Configuration
public class Config {
@Bean(name = "configRed")
public Red getRedInstance() {
    Red red = new Red();
    red.setName("Config red");
    return red;
}
@Bean(name = "configBlack")
public Black getBlackInstance() {
    Black black = new Black();
    black.setName("config Black");
    return black;
}
@Bean(name = "constRed")
public Red getConstRedInstance() {
    Red red = new Red();
    red.setName("Config const red");
    return red;
}
@Bean(name = "constBlack")
public Black getConstBlackInstance() {
    Black black = new Black();
    black.setName("config const Black");
    return black;
}
}

bootapplication(主类):

@SpringBootApplication
@ComponentScan(basePackages = {"com"})
public class BootApplication {
public static void main(String[] args) {
    SpringApplication.run(BootApplication.class, args);
}
}

运行应用程序并点击URL: 获取127.0.0.1:8080/abc/dev/customers/

Output:
Injecting setter
Field injection red: Config red
Field injection: null
Setter injection black: config Black
Constructor inject nred: Config const red
Constructor inject nblack: config const Black
Red color: No injection red
Injecting setter
No injection : No injection red

带有示例?这是一个简单的:

public class TwoInjectionStyles {
    private Foo foo;
    // Constructor injection
    public TwoInjectionStyles(Foo f) {
        this.foo = f;
    }
    // Setting injection
    public void setFoo(Foo f) { this.foo = f; }
}

就我个人而言,我更喜欢构造函数注入。

在这两种情况下,Bean Factory都实例化TwoInjectionStylesFoo实例,并赋予前者的Foo依赖关系。

最新更新