web-dev-qa-db-ja.com

コンストラクターの注入が他のオプションよりも優れている理由を説明する

Pro Spring 3 Book、第4章-はじめにIOCおよびSpringのDI-ページ59の「セッターインジェクションとコンストラクターインジェクション」セクションの段落

Springが含まれ、Setter Injectionを使用するときにすべての依存関係が定義されるようにするメカニズムを提供しますが、Constructor Injectionを使用することで、コンテナーに依存しない方法で依存関係の要件をアサートします。

例で説明してもらえますか

61
minil

必要な依存関係をコンストラクター引数として取るクラスは、その引数が提供されている場合にのみインスタンス化できます(引数がnullでないことを確認するためにガード句が必要です)。したがって、コンストラクターは、 Springを使用して、コンテナに依存しないようにします。

セッターインジェクションを使用する場合、セッターは呼び出される場合と呼び出されない場合があるため、インスタンスに依存関係が提供されることはありません。セッターを強制的に呼び出す唯一の方法は、@Requiredまたは@Autowiredを使用することです。これは、Springに固有であり、コンテナに依存しません。

したがって、コードをSpringから独立させるには、インジェクションにコンストラクター引数を使用します。

Update:Spring 4.3は 単一コンストラクターのシナリオでの暗黙的なインジェクション を実行し、潜在的に必要としないことでコードをSpringからより独立させます@Autowiredアノテーション。

79

(...)Constructor Injectionを使用して、コンテナに依存しない方法で依存関係の要件をアサートします

これは、コンテナ固有のソリューションを使用せずに、注入されたすべてのフィールドに要件を適用できることを意味します


セッター注入の例

セッターインジェクションでは、特別なスプリングアノテーション@Requiredが必要です。

@ Required

メソッド(通常はJavaBeanセッターメソッド)を 'required'としてマークします。つまり、セッターメソッドは値を依存性注入するように構成する必要があります。

使用法

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

}
16
MariuszS

簡単にするために、必須の依存関係にはコンストラクターベースの依存性注入を使用し、オプションの依存関係にはセッターベースの注入を使用できるとしましょう。それは経験則です!!

たとえば、.

クラスをインスタンス化する場合は、常にコンストラクターでそれを行います。したがって、コンストラクタベースのインジェクションを使用している場合、クラスをインスタンス化する唯一の方法はそのコンストラクタを使用することです。コンストラクターを介して依存関係を渡すと、それが必須の依存関係であることが明らかになります。

一方、POJOクラスにセッターメソッドがある場合、そのセッターメソッドを使用してクラス変数の値を設定する場合としない場合があります。それは完全にあなたのニーズに基づいています。つまり、オプションです。したがって、クラスのセッターメソッドを介して依存関係を渡すと、暗黙的にオプションの依存関係であることを意味します。これが明確であることを願っています!!

11
Jawa

コンストラクター注入は、クラスが依存クラスなしでは機能できない場合に使用されます。

クラスが依存クラスなしで機能できる場合、プロパティインジェクションが使用されます。

具体的な例として、IServiceに依存して作業を行うServiceRepositoryを考えます。 ServiceRepositoryはIServiceなしでは有効に機能できないため、コンストラクタを介して注入するのが理にかなっています。

同じServiceRepositoryクラスは、ロガーを使用してトレースを実行できます。 ILoggerは、プロパティインジェクションを介してインジェクトでき​​ます。

プロパティインジェクションのその他の一般的な例は、ICache(AOP用語の別の側面)またはIBaseProperty(基本クラスのプロパティ)です。

5
vijayst

Constructor Injectionを使用することにより、コンテナに依存しない方法で依存関係の要件をアサートします

IoCコンテナから、Beanを使用する前に必要なBeanの注入を行う必要があるという保証が必要です。

setter injection戦略では、最初にBeanを作成するが、setterメソッドを使用してBeanを使用する直前に注入を行うというIoCコンテナーを信頼します。そして、注入は設定に従って行われます。設定で注入するBeanの指定を何らかの形でミスした場合、それらのBeanに対して注入は行われず、使用中の依存Beanはそれに応じて機能しません!

しかし、constructor injection戦略では、コンテナはBeanの構築中に依存関係を適切に提供するように強制します(または強制する必要があります)。これは "コンテナに依存しない方法"として対処されました。Beanの作成中に依存関係を提供する必要があるため、 IoCコンテナに依存しない依存関係の可視性。

Edit:

Q1:そして、Beanが欠落する代わりにnullの値を持つコンストラクターによってコンテナーがBeanを作成しないようにする方法は?

Beanを作成するために提供されたコンストラクターと一致するために必要なすべてのコンストラクター引数を提供するIoCコンテナーによって課せられるため、<constructor-arg>(Springの場合)を実際に見逃すオプションはありません。 <constructor-arg>に意図的にnullを指定した場合。その場合、IoCコンテナでできることや行う必要はありません!

4

この例が役立つ場合があります。

コントローラークラス:

@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をヒット:GET 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
3
quintin

例付き?以下に簡単なものを示します。

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依存関係を与えます。

0
duffymo