[ Spring ] DI, Dependency Injection

듀듀·2023년 1월 18일
0

[Spring 뽀개기]

목록 보기
1/1
post-thumbnail

[ 생성자 주입 vs 필드 주입(@Autowired) ]

스프링에서 의존성을 주입하는 방법은 3가지가 있다.

  1. 생성자 주입 (Constructor Injection)
  2. 필드 주입 (Field Injection)
  3. 수정자 주입 (Setter Injection)

DewCoding 빈에 Dew 빈을 주입하는 예제

1. 생성자 주입

@Component
public class Dewcoding {
	private final Dew dew;
    
    public DewCoding(Dew dew){
    	this.dew = dew;
    }
}

단일 생성자의 경우 @Autowired 어노테이션을 붙이지 않아도 되지만, 생성자가 2개 이상인 경우에는 생성자에 @Autowired 어노테이션을 붙여줘야 한다.


@Component
@RequiredArgsConstructor
public class Dewcoding {
	private final Dew dew;
}

@RequiredArgsConstructor 어노테이션은 final이나 @NotNull을 사용한 필드에 대한 생성자를 자동으로 생성해준다.

2. 필드 주입

@Component
public class Dewcoding {

	@Autowired
	private Dew dew;
}

생성자 주입과 달리 필드를 final 선언할 수 없다.


3. 수정자 주입

@Component
public class Dewcoding {

	private Dew dew;
    
    @Autowired
    public void setDew(Dew dew){
    	this.dew = dew;
    }
}

생성자 주입이 아닌 다른 방식으로 주입하면 인텔리제이는 다음과 같은 경고를 준다.

Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencied".

대충 생성자 주입 방식을 추천한다는 뜻.


생성자 주입 방식의 장점

1. 순환 참조 방지

객체의 의존성을 추가하다 보면 순환 참조 문제가 발생할 수 있다.
순환 참조란 A가 B를 참조하면서, B도 A를 참조하는 경우 발생한다.

@Component
public class Dew{
	
    @Autowired
    private Coding coding;
    
    public void dew(){
    	coding.coding();
    }
}

@Component
public class Coding{
	
    @Autowired
    private Dew dew;
    
    public void coding(){
    	dew.dew();
    }
}

Dewcoding의 빈을 하나씩 생성 후, 서로 필드 주입한고 이 둘을 DewCoding에 주입한다.

@Component
public class DewCoding{
	
    @Autowired
    private Dew dew;
    
    @Autowired
    private Coding coding;
    
    public void dewCoding() {
    	dew.dew();
        coding.coding();
    }
}

서버를 실행 한 후 dewCoding() 메서드가 나오면 순환참조로 서버가 다운된다.
메소드 실행 전까지는 순환 참조가 있는걸 알 수 없다.

따라서 순환 참조를 방지하기 위해서는 생성자 주입을 해야한다.

위의 예제를 생성자 주입으로 변경하면 다음과 같다.

@Component
@RequiredArgsConstructor
public class Dew{
	
    private final Coding coding;
    
    public void dew(){
    	coding.coding();
    }
}

@Component
@RequiredArgsConstructor
public class Coding{
	
    private final Dew dew;
    
    public void coding(){
    	dew.dew();
    }
}

@Component
@RequiredArgsConstructor
public class DewCoding{
	
    private final Dew dew;
    private final Coding coding;
    
    public void dewCoding(){
    	dew.dew();
        coding.coding();
    }
}

생성자 주입으로 변경하게 되면 순환 참조가 있을 때 서버 자체가 구동 되지 않는다.

생성자 주입 방식은 필드와 수정자 방식가 다르게 먼저 빈을 생성하지 않고 주입하려는 빈을 먼저 찾기 때문에 참조하는 객체가 생성되지 않은 상태에서 그 빈을 참조하기 때문에 오류가 발생하는 것이다.

2. final 선언 가능

필드를 final로 선언할 수 있기 때문에 런타임에 객체 불변성을 보장한다.

3. 테스트 코드 작성 용이

스프링 컨테이너 도움 없이 테스트 코드를 더 편리하게 작성할 수 있다.
테스트하고자 하는 클래스에 필드 주입이나 수정자 주입으로 빈이 주입되어 있으면, Mockito를 이용해 목킹한 후 테스트를 진행해야 한다.
하지만, 생성자 주입의 경우는 단순히 원하는 객체를 생성한 후, 생성자에 넣어주면 된다.

public class DewTest{
	@Test
    public void test(){
    	Dew dew = new Dew();
        DewCoding dewCoding = new DewCoding(dew);
        dew.dew();
    }
}

출처

profile
나는 내 의지대로 된다.

0개의 댓글