program story

생성자 주입이 다른 옵션보다 나은 이유 설명

inputbox 2020. 11. 24. 07:58
반응형

생성자 주입이 다른 옵션보다 나은 이유 설명


이 질문에 이미 답변이 있습니다.

Pro Spring 3 Book, 4 장-Spring의 IOC 및 DI 소개-59 페이지, "Setter Injection vs. Constructor Injection"섹션에서 단락이 말합니다.

Spring이 포함되어 Setter Injection을 사용할 때 모든 종속성이 정의되도록하는 메커니즘을 제공하지만 Constructor Injection을 사용하면 컨테이너에 구애받지 않는 방식으로 종속성에 대한 요구 사항을 주장 할 수 있습니다. "

예를 들어 설명해 주시겠습니까


필수 종속성을 생성자 인수로 사용하는 클래스는 해당 인수가 제공된 경우에만 인스턴스화 할 수 있습니다 (인수가 null이 아닌지 확인하려면 guard 절이 있어야합니다.) 따라서 생성자는 사용자가 아닌지 여부에 관계없이 종속성 요구 사항을 적용합니다. Spring을 사용하여 컨테이너에 구애받지 않습니다.

setter 주입을 사용하는 경우 setter가 호출되거나 호출되지 않을 수 있으므로 인스턴스에 종속성이 제공되지 않을 수 있습니다. setter를 강제로 호출하는 유일한 방법은 @Requiredor @Autowired를 사용하는 것입니다. 이는 Spring에 고유하므로 컨테이너에 구애받지 않습니다.

따라서 코드를 Spring과 독립적으로 유지하려면 삽입에 생성자 인수를 사용하십시오.

업데이트 : Spring 4.3은 단일 생성자 시나리오에서 암시 적 주입을 수행 하여 잠재적으로 @Autowired주석이 전혀 필요하지 않으므로 코드를 Spring과 더 독립적으로 만듭니다.


(...) Constructor Injection을 사용하여 컨테이너에 구애받지 않는 방식으로 종속성에 대한 요구 사항을 주장합니다.

즉, 컨테이너 별 솔루션을 사용하지 않고도 삽입 된 모든 필드에 대한 요구 사항을 적용 할 수 있습니다 .


세터 주입 예

세터 주입의 경우 특수 스프링 주석 @Required이 필요합니다.

@ 필수

메소드 (일반적으로 JavaBean setter 메소드)를 ' required '로 표시합니다. 즉, 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;
    }

}

간단하게하기 위해, 필수 종속성에 대해 생성자 기반 종속성 주입을 사용하고 선택적 종속성에 대해 setter 기반 주입을 사용할 수 있다고 가정 해 보겠습니다. 그것은 경험의 법칙입니다 !!

예를 들어 봅시다.

클래스를 인스턴스화하려면 항상 생성자로 수행합니다. 따라서 생성자 기반 주입을 사용하는 경우 클래스를 인스턴스화하는 유일한 방법은 해당 생성자를 사용하는 것입니다. 생성자를 통해 종속성을 전달하면 필수 종속성이라는 것이 분명해집니다.

반면에 POJO 클래스에 setter 메서드가있는 경우 해당 setter 메서드를 사용하여 클래스 변수에 대한 값을 설정할 수도 있고 설정하지 않을 수도 있습니다. 그것은 전적으로 귀하의 필요에 기반합니다. 즉, 선택 사항입니다. 따라서 클래스의 setter 메서드를 통해 종속성을 전달하면 암시 적으로 선택적 종속성임을 의미합니다. 이것이 분명하길 바랍니다 !!


생성자 주입은 클래스가 종속 클래스없이 작동 할 수 없을 때 사용됩니다.

속성 주입은 클래스가 종속 클래스없이 작동 할 수있을 때 사용됩니다.

구체적인 예로서 IService에 의존하여 작업을 수행하는 ServiceRepository를 고려하십시오. ServiceRepository는 IService 없이는 유용하게 작동 할 수 없으므로 생성자를 통해 주입하는 것이 좋습니다.

동일한 ServiceRepository 클래스가 Logger를 사용하여 추적을 수행 할 수 있습니다. ILogger는 속성 주입을 통해 주입 할 수 있습니다.

속성 주입의 다른 일반적인 예는 ICache (AOP 용어의 또 다른 측면) 또는 IBaseProperty (기본 클래스의 속성)입니다.


생성자 주입을 사용하여 컨테이너에 구애받지 않는 방식으로 종속성에 대한 요구 사항을 주장합니다.

Bean을 사용하기 전에 필요한 Bean을 주입해야한다는 IoC 컨테이너의 보증이 필요합니다.

에서 세터 주입 전략, 우리는 먼저 먼저 콩을 만들 것입니다하지만 setter 메소드를 사용하여 빈을 사용하기 전에 주입 권리를 할 것이라는 IoC 컨테이너를 신뢰합니다. 그리고 주입은 구성에 따라 수행됩니다. 어떻게 든 구성에 주입 할 빈을 지정하지 않으면 해당 빈에 대해 주입이 수행되지 않으며 종속 빈이 사용 중일 때 그에 따라 작동하지 않습니다!

그러나 생성자 주입 전략에서 컨테이너는 빈을 구성하는 동안 종속성을 적절하게 제공하기 위해 부과 (또는 부과해야 함)합니다. 이는 빈을 생성하는 동안 종속성을 제공해야 하므로 "컨테이너에 구애받지 않는 방식"으로 해결되었습니다 . 따라서 IoC 컨테이너와 관계없이 종속성을 표시 할 수 있습니다.

편집하다:

Q1 : 컨테이너null가 누락 된 빈 대신 값을 사용하여 생성자에 의해 빈을 생성하는 것을 방지하는 방법은무엇입니까?

<constructor-arg>IoC 컨테이너에 의해 빈 생성을 위해 제공된 생성자와 일치하는 데 필요한 모든 생성자 인수를 제공하기 때문에 실제로 (Spring의 경우) 놓칠 수있는 옵션이 없습니다 . 당신이 제공하는 경우 null귀하에 <constructor-arg>의도적으로. 그렇다면 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 : 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

예를 들어? 다음은 간단한 것입니다.

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

개인적으로 가능한 경우 생성자 주입을 선호합니다.

두 경우 모두 빈 팩토리는 TwoInjectionStylesFoo인스턴스를 인스턴스화 하고 이전에 Foo종속성을 제공합니다 .

참고URL : https://stackoverflow.com/questions/21218868/explain-why-constructor-inject-is-better-than-other-options

반응형