spring 복습 2일차
IoC와 DI를 좀 더 공부하려고 한다.
- 프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴을 말한다.
- 전통적인 프로그래밍에서 흐름은 프로그래머가 작성한 프로그램이 외부 라이브러리의 코드를 호출해 이용한다. 하지만 제어 반전이 적용된 구조에서는 외부 라이브러리의 코드가 프로그래머가 작성한 코드를 호출한다.
- 오브젝트(Bean) 생성, 관계설정, 사용, 제거 등 오브젝트 전반에 걸친 모든 제어권을 애플리케이션이 갖는게 아니라 프레임워크의 컨테이너에게 넘기는 개념
- 작업을 구현하는 방식과 작업 수행 자체를 분리한다.
- 모듈을 제작할 때, 모듈과 외부 프로그램의 결합에 대해 고민할 필요 없이 모듈의 목적에 집중할 수 있다.
- 다른 시스템이 어떻게 동작할지에 대해 고민할 필요 없이, 미리 정해진 협약대로만 동작하게 하면 된다.
- 모듈을 바꾸어도 다른 시스템에 부작용을 일으키지 않는다.
- 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴
- 객체를 직접 생성하는 것이 아니라 외부에서 생성 후 주입시켜 주는 방식
- DI를 통해 객체 간의 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있다.
DI의 3가지 방법
- 필드 주입
- 메서드(Setter) 주입
- 생성자 주입
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.food = new Chicken();
consumer.eat();
consumer.food = new Pizza();
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
Food를 직접 Consumer에 포함시키고 Food에 필요한 객체를 주입받아 사용.
public class Consumer {
@Autowired
private Food food;
}
- @Autowired를 사용하면 자동으로 의존관계 주입, 편하게 의존성 주입 가능
- 주입하기 쉬운만큼 제한없이 추가될 수 있어 위험하다.(단일 책임의 원칙(SRP) 위반
- 의존성이 숨는다, 즉 참조 관계를 눈으로 확인하기 어렵고 순환참조를 막을 수가 없다.
- 생성자 이후에 호출되므로, 필드에 final키워드를 사용할 수 없어 객체가 변할 수 있다.
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public void setFood(Food food) {
this.food = food;
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.setFood(new Chicken());
consumer.eat();
consumer.setFood(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
Set메서드를 이용하여 필요한 객체를 주입 받아사용
public class Consumer {
private Food food;
@Autowired
public void setFood(Food food) {
this.food = food;
}
}
- @Autowired 어노테이션과 set 메서드를 통해 의존관계 주입
- NullPointerException이 발생할 수 있다.
- 생성자 이후에 호출되므로, 필드에 final키워드를 사용할 수 없어 객체가 변할 수 있다.
public class Consumer {
Food food;
public Consumer(Food food) {
this.food = food;
}
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer(new Chicken());
consumer.eat();
consumer = new Consumer(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
생성자를 사용하여 필요한 객체를 주입
private Food food;
public Consumer(Food food) {
this.food = food;
}
}
- spring에서 가장 권장하는 방식
- 다른 DI방법과 다르게 final키워드 사용이 가능하다.
- NullPointerException이 발생하지 않는다.
- 순환의존성을 알 수 있다.
- @Autowired 생략이 가능하고, Lombok라이브러리를 사용해 생성자 또한 생략이 가능하다.