스프링 프레임워크는 Java기반의 웹 어플리케이션을 개발할때 사용하는프레임워크입니다.
스프링에는 POJO(순수 객체), AOP(관점 중심 프로그래밍), Portable Service Abstraction(이식 가능 서비스 추상화),DI(의존성 주입) 등의 중요 개념이 있습니다. 그 중에서 오늘은 DI(의존성 주입)
에 대해 알아보려 합니다.
그럼 시작해보겠습니다😀
먼저 예시 코드를 살펴보겠습니다.
public class PetOwner{
private AnimalType animal;
public PetOwner(){
this.animal = new Dog();
}
}
interface AnimalType{}
class Dog implements AnimalType{}
class Cat implements AnimalType{}
PetOwner 클래스의 생성자를 살펴보면 animal
이라는 필드가 Dog
타입으로 생성되는 코드가 있습니다. 만약 animal
필드를 Dog
가 아닌 Cat
으로 생성하려 한다면 코드 자체의 수정이 필요합니다. 위와 같은 경우는 PetOwner 클래스가 AnimalType에 의존적이라고 볼 수 있습니다. 스프링에서는 앞으로 설명할 의존성 주입을 통해 객체간의 의존성을 줄여줍니다.
"스프링 컨테이너에서 객체 Bean을 먼저 생성해두고 생성한 객체를 지정한 객체에 주입하는 방식을 의존성 주입 이라고 합니다"
객체 자체가 코드 상에서 객체 생성에 관여하지 않아도 되기때문에 객체 사이의 의존도를 낮출수 있습니다. 스프링 컨테이너에서 객체의 생명주기를 관리하며 객체의 의존관계 또한 관리해줍니다. 의존성 주입을 사용하면 유연하고 확장성이 뛰어난 코드 작성이 가능해집니다! 그렇기에 의존성 주입은 스프링 프레임워크에서 가장 중요한 개념이라고 해도 과언이 아닙니다.
중요성을 알게되었다면 이제 사용 방식에 대해 알아봐야합니다. 사용 방식은 다음의 3가지입니다.
필드 주입은 클래스에 선언된 필드에 생성된 객체를 주입해주는 방식입니다.
필드에 주입할때는 어노테이션을 사용합니다. 스프링에서 제공하는 @Autowired
어노테이션을 주입할 필드위에 명시해줍니다.
@Controller
public class PetController{
@Autowired
private PetService petService;
}
수정자 주입은 클래스의 수정자를 통해서 의존성을 주입해주는 방식입니다.
@Controller
public class PetController{
private PetService petService;
@Autowired
public void setPetService(PetService petService){
this.petService = petService;
}
}
생성자 주입은 클래스의 생성자를 통해서 의존성을 주입해주는 방식입니다.
생성자 주입은 인스턴스가 생성될때 1회 호출되는것이 보장됩니다.
생성자 주입시 필드에 final
키워드를 사용할 수 있습니다.
@Controller
public class PetController{
private final PetService petService;
@Autowired
public PetController(PetService petService){
this.petService = petService;
}
}
생성자 주입시에는 다음과 같은,
두 특징과 Lombok 라이브러리를 통해서 더 간편하게 작성이 가능합니다.
@Controller
@RequiredArgsConstructor
public class PetController{
private final PetService petService;
}
@RequiredArgsConstructor
로 필드를 포함한 생성자를 포함시켜주고 @Autowired
키워드를 생략해서 더 가독성이 좋은 코드로 사용이 가능합니다.
스프링에서는 생성자 주입 방식을 권장하고 있습니다.
IntelliJ를 사용한다면 필드 주입을 사용할때 경고줄이 나타나는 것을 확인하실 수 있습니다.
생성자 주입 방식을 권장하는 이유는 다음과 같습니다.
생성자 주입 방식을 사용할 경우 객체가 생성되는 시점에 생성자를 호출하여 최초 1회만 주입합니다. 이와 같은 특성으로 불변(immutable) 객체를 보장합니다. 또한 객체를 포함한 클래스가 생성되는 시점에 포함된 객체들도 반드시 생성되기 때문에 객체가 비어있을 가능성도 배제합니다.
순환 참조란 A,B 두 객체가 각각 서로를 필드에 포함하여 참조하고 있는 상태를 말합니다.
서로가 서로를 참조하고 있기 때문에 A,B 두 클래스가 맞물려서 서로의 객체를 계속 생성하는 무한반복 상태에 빠지는 문제가 발생합니다. 3가지 방식 모두 순환 참조 문제가 발생하지만 발생 시점이 다릅니다.
필드주입, 수정자주입은 실제 메소드가 호출되었을때 runtime 에러와 함께 수정자 주입 문제가 불거지게 됩니다. runtime에 에러가 발생하게 되면 미리 예측이 어렵기 때문에 서비스 진행중에 문제를 야기할 수 있습니다.
생성자 주입은 스프링 어플리케이션이 구동되는 순간에 에러가 발생합니다. 즉 컴파일 타임에 에러가 발생하는 것이죠. 컴파일 타임에 발생하는 에러는 개발자가 쉽게 추적이 가능하기 때문에 서비스 진행전 미리 예방할 수 있습니다.
단위 테스트를 진행할때 순수 자바 코드로 테스트가 가능합니다. 필드 주입을 사용하는 경우에 순수 자바 코드에서는 DI가 이루어지지않기 때문에 필드가 null 상태가 되어 에러가 발생합니다.