Spring IoC Container가 Bean을 관리할 때는 객체관리 뿐아니라 객체 내에 필요한 객체를 의존성 주입으로 해결한다. 의존성 주입은 필요한 객체를 생성하는 것이 아닌 외부로 부터 객체를 받아 사용하는 것이다.
관련 작성 포스팅 의존성 주입은 왜 필요한가?
스프링은 @Autowired 어노테이션을 이용한 다양한 의존성 주입 방법을 제공하는데 종류는 생성자, 수정자, 필드로 나뉜다.
필드주입
1. 코드가 간결하다.
2. 외부에서 변경이 불가능해 테스트시 모킹이 필요하다
3. 스프링 컨테이너 없이 의존성 주입이 힘들다
4. 객체가 생성된 이후에 주입하므로 final 키워드를 사용할 수 없어 해당 객체의 불변을 보장하지 못한다
@Service
public class ProductService {
@Autowired
private final ProductRepository productRepository;
}
수정자(setter)
1. 객체가 생성된 이후에 주입하므로 final 키워드를 사용할 수 없어 해당 객체의 불변을 보장하지 못한다
2. 불변성을 보장하지 못하는데 setter가 public으로 열려있어 객체가 바뀔 위험이 더 크다
@Service
public class ProductService {
private ProductRepository productRepository;
@Autowired
public void setProductRepository(ProductReposiotry repo){
productRepository = repo;
}
}
생성자
1. 객체 생성시에 의존성을 주입해 final 키워드를 사용할 수 있어 불변성을 보장한다.
2. 스프링 컨테이너에 의존하지 않고 순수 자바로 주입해줄 수 있다.
3. 2와 같은 이유로 테스트를 순수 자바로 할 수 있다.
@Service
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}
추천 방법(생성자)
@Component
public class A {
@Autowired
private B b;
public void doAA() {
b.doBB();
}
}
@Component
public class B {
@Autowired
private A a;
public void doBB() {
a.doAA();
}
}
@Component
public class C {
@Autowired
private A a;
@Autowired
private B b;
public void doCC() {
a.doAA();
b.doBB();
}
}
필드와 세터주입은 필드나 수정자 주입은 빈이 생성된 이후에 참조를 하기 때문에 순환참조가 있는 객체를 사용하기 전까지 예외발생을 일으키지 않는다.
@Component
public class A {
private B b;
@Autowired
public A(B b) {
super();
this.b = b;
}
public void doAA() {
b.doBB();
}
}
@Component
public class B {
@Autowired
public B(A a) {
super();
this.a = a;
}
private A a;
public void doBB() {
a.doAA();
}
}
정리
순환참조와 final 키워드를 통한 불변성 보장, 프레임워크에 의존하지 않는 순수자바로 주입이 가능한 이유들로 스프링도 생성자 주입을 권장한다