Spring IoC
IoC를 이론적으로는 알지만, 자꾸 실무에서 왜 쓰는지 헷갈려서 다시 한 번 정리해본다.
IoC(Inversion of Control)
제어권이 역전되었다는 뜻이다.
예제를 통해 알아보겠다.
@Service
public class SampleService {
private SampleRepository sampleRepository = new SampleRepository();
}
SampleService 클래스는 SampleRepository에 의존한다.
SampleService가 SampleRepository를 이렇게 직접 만들어서 사용하는 게 아니라
@Service
public class SampleService {
private SampleRepository sampleRepository;
public SampleService(SampleRepository sampleRepository) {
this.sampleRepository = sampleRepository;
}
}
public class SampleServiceTest {
@Test
public void save() {
SampleRepository sampleRepository = new SampleRepository;
SampleService sampleService = new SampleService(sampleRepository);
}
}
생성자라는 것을 통해서 외부로부터 의존성을 주입받는 것을 Inversion of Control이라고 한다.
IoC 컨테이너라는 스프링이 제공해주는 사용해서 의존성을 주입받는 것이다.
컨테이너 안에는 빈이 들어있다. 빈은 컨테이너 안에 있는 객체들을 의미한다. 컨테이너는 IoC 기능을 제공하므로 IoC 컨테이너고, 컨테이너가 의존성 주입 즉, 객체를 만들어서 빈으로 넣어놓고 우리는 이 빈들을 컨테이너로부터 가져와서 사용하는 것이다.
더 발전해서 어노테이션 기반의 DI를 제공해서 @Service, @Controller, @AutoWired 등 선언해주기만 하면 알아서 객체를 생성해준다.
DI 방식
- 필드 주입
public class Sample {
@Autowired
private A a;
}
1. 주입받으려는 빈의 생성자를 호출해 빈을 찾거나 빈 팩토리에 등록
2. 생성자 인자에 사용하는 빈을 찾거나 만듦
3. 필드에 주입
- 세터 주입
public class Sample {
private A a;
@Autowired
public void setA(A a){
this.a = a;
}
}
1. 주입받으려는 빈의 생성자를 호출해 빈을 찾거나 빈 팩토리에 등록
2. 생성자 인자에 사용하는 빈을 찾거나 만듦
3. 주입하려는 빈 객체의 세터(수정자)를 호출하여 주입
위 두 방식은 런타임에서 의존성을 주입하므로 의존성을 주입하지 않아도 객체가 생성될 수 있다.
Sample과 A의 생성?주입? 시기가 다르다.
- 생성자 주입
public class Sample {
private final A a;
public void Sample(A a){
this.a = a;
}
}
1. 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리에서 만듦
2. 찾은 인자 빈으로 주입하려는 생성자 호출
객체가 생성되는 시점에 빈을 주입한다.
생성자를 이용하면 순환참조를 방지한다.(순환참조 시 에러가 아닌 구동조차 되지 않음)
오류를 방지한다.(final를 선언해 초기화 후 빈 객체가 변경될 수 없다.)
NullPointerException을 방지한다.(런타임 때 생성되는 위 두 개의 경우라면 런타임 시점에 null이 되어버린다면 널 포인터 익셉션이 발생한다. 생성자 주입에는 컴파일 시에 오류를 알 수 있다.)
즉, 생성자 주입을 권장한다.
https://isoomni.tistory.com/entry/TISPRING-IOC-DI-%EC%A0%95%EC%9D%98-%EC%9E%A5%EC%A0%90
[TI/SPRING] IOC, DI 정의/ 장점
[Tech Interview] IoC, DI는 무엇이고 어떠한 장점이 있을까요? IOC 란 무엇인가? IOC 는 SPRING 용어가 아닙니다.SPRING 이전에도 존재했던 용어죠. IOC는 Inversion of Control 의 약자입니다. 단어를 좀 뜯어볼..
isoomni.tistory.com
이게 실무에서 결합도를 낮추고, 코드 중복이나 유지보수에 편하다고 하는 이유는?
public class A {
private B b = new B();
}
만약 A라는 클래스를 만들 때 B라는 클래스를 주입해줘야 해서 이렇게 직접 주입으로 해줬는데, 즉 A는 B에 의존하는데
나중에 B 객체가 C라는 객체에 의존하게 되는 걸로 바뀌었다면(B 클래스는 생성자를 이용해 주입해주는 걸로 해줌)
public class A {
private C c = new C();
private B b = new B(c);
}
이렇게 또 코드를 바꾸어야 한다. 이러면 개발자는 유지보수가 어려워진다. 또한, 신경쓸 게 많다.
그렇다면 생성자 주입으로 바꿔보자
public class A{
private final B b;
public void A(B b){
this.b = b;
}
}
A라는 객체가 생성될 때 생성자를 이용해 B 객체를 외부로부터 주입받겠다고 정의했다.
만약 B가 C에 의존한다고 바뀌어도 A 클래스는 바뀔 것이 없다. 왜냐면 외부로부터 주입받기 때문이다.
그리고 그대로인 코드에서 A를 생성할 때 B 의존성 문제로 오류가 나지는 않는다.
그래서 결합도를 낮추고, 코드 중복이나 유지보수에 편하다.
누군가가 이 글을 봤을 때, 제가 이해한 개념이 틀리다면 댓글을 달아주시면 감사하겠습니다..