Java 解释为什么构造函数注入优于其他选项

Java 解释为什么构造函数注入优于其他选项,java,spring,dependency-injection,required,Java,Spring,Dependency Injection,Required,在一本支持Spring 3的书中, 第4章-介绍IOC和DI在春季-第59页,在“Setter注入与构造函数注入”一节中,一段说 包括Spring,提供了一种机制来确保在 您可以使用Setter注入,但通过使用构造函数注入,您可以以容器无关的方式断言依赖项的需求” 你能举例说明吗?这里有一个简单的例子: public class TwoInjectionStyles { private Foo foo; // Constructor injection public T

在一本支持Spring 3的书中, 第4章-介绍IOC和DI在春季-第59页,在“Setter注入与构造函数注入”一节中,一段说

包括Spring,提供了一种机制来确保在 您可以使用Setter注入,但通过使用构造函数注入,您可以以容器无关的方式断言依赖项的需求”


你能举例说明吗?这里有一个简单的例子:

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工厂实例化
TwoInjectionStyles
Foo
实例,并为前者提供其
Foo
依赖关系

(…)通过使用构造函数注入,您可以以容器无关的方式断言依赖项的需求

这意味着您可以强制执行所有注入字段的要求,而无需使用任何特定于容器的解决方案


塞特注入示例 对于setter注入,需要特殊的弹簧注释
@必需

@必填项

将方法(通常是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;
    }
}
import javax.inject.Inject;
import javax.inject.Named;

@Named
public class Foo {

    private Bar bar;

    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }

}
构造函数注入示例 所有必填字段都在构造函数(纯Java解决方案)中定义

用法

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;
    }
}
import javax.inject.Inject;
import javax.inject.Named;

@Named
public class Foo {

    private Bar bar;

    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }

}
单元测试
这在单元测试中特别有用。这类测试应该非常简单,不理解像
@Required
这样的注释,它们通常不需要Spring来运行简单的单元测试。当使用构造函数时,为测试设置这个类要容易得多,不需要分析测试中的类是如何实现的d、

将所需依赖项作为构造函数参数的类只能在提供该参数的情况下实例化(您应该有一个guard子句以确保该参数不为null)。因此,无论您是否使用Spring,构造函数都会强制执行依赖项要求,使其与容器无关

如果使用setter注入,则setter可能会被调用,也可能不会被调用,因此实例可能永远不会提供其依赖项。强制调用setter的唯一方法是使用
@Required
@Autowired
,这是特定于Spring的,因此不是容器不可知论的

因此,为了使代码独立于Spring,请使用构造函数参数进行注入

更新:Spring 4.3将执行,通过根本不需要
@Autowired
注释,使您的代码更加独立于Spring

通过使用构造函数注入,可以以与容器无关的方式断言依赖项的需求

我们需要来自IoC容器的保证,即在使用任何bean之前,必须注入必要的bean

setter注入策略中,我们相信IoC容器会首先创建bean,但会在使用setter方法使用bean之前进行注入。注入是根据您的配置完成的。如果您在配置中没有指定要注入的任何bean,则注入将我不会为那些bean做任何事情,当它被使用时,你的依赖bean也不会相应地起作用

但是在构造函数注入策略中,容器强制(或必须强制)在构造bean时正确地提供依赖项“,因为我们需要在创建bean时提供依赖项,从而使依赖项的可见性独立于任何IoC容器

编辑:

Q1:如何防止容器通过构造函数使用
null
值而不是缺少bean来创建bean


您没有选择错过任何
(在Spring的情况下),因为IoC容器强制您提供与创建bean所提供的构造函数匹配所需的所有构造函数参数。如果您故意在
中提供
null
。那么,IoC容器就没有什么可以做或需要做的了

简单地说,我们可以使用基于构造函数的依赖项注入实现强制依赖项,使用基于setter的注入实现可选依赖项。这是一条经验法则

比如说

如果你想实例化一个类,你总是用它的构造函数。因此,如果您使用的是基于构造函数的注入,那么实例化类的唯一方法就是通过该构造函数。如果您通过构造函数传递依赖项,那么很明显它是一个强制依赖项


另一方面,如果POJO类中有setter方法,则可以使用该setter方法为类变量设置值,也可以不使用该方法。这完全是基于你的需要。i、 它是可选的。因此,如果通过类的setter方法传递依赖项,则隐式表示它是可选依赖项。希望这是清楚的

当类没有依赖类就无法运行时,使用构造函数注入

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

作为一个具体的例子,考虑一个依赖于ISService的Service PosiPoad来完成它的工作。由于ServiceRepository在没有iSeries的情况下无法有效运行,因此通过构造函数注入它是有意义的

同一ServiceRepository类可以使用记录器进行跟踪。ILogger可以通过属性注入进行注入

属性注入的其他常见示例是ICache(AOP术语中的另一个方面)或IBaseProperty(基本c中的一个属性)
@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");
}

}
@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;
}
}
@SpringBootApplication
@ComponentScan(basePackages = {"com"})
public class BootApplication {

public static void main(String[] args) {
    SpringApplication.run(BootApplication.class, args);
}
}
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