[Spring] 스프링 컨테이너 - ApplicationContext, IoC, DI
이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다!
<순차>
1. IoC란?
2. DI란?
3. ApplicationContext : 스프링 컨테이너란?
4. 스프링 컨테이너의 생성과 빈 등록 과정
5. @Configuration과 싱글톤 컨테이너
⛅️ IoC란?
IoC 제어의 역전이란 Inversion of Control의 약자로, 프로그램의 제어를 다른 대상이 맡는 것을 말한다.
스프링의 경우에는 스프링 컨테이너가 Bean의 생성, 의존관계주입과 같은 작업을 담당한다.
여기서 스프링 컨테이너는 ApplicationContext이며, IoC 컨테이너 혹은 DI 컨테이너라고도 부른다.
스프링은 포함한 프레임워크는 내가 작성한 코드를 제어하고, 대신 실행한다.
⛅️ DI : 의존관계주입 이란?
DI는 Dependency Injection의 약자로, 우리말로는 의존 주입이라고 한다.
여기서 말하는 의존은 객체 간의 의존을 의미한다.
한 클래스(A)가 다른 클래스(B)의 메서드를 실행할 때, A 클래스가 B 클래스에 의존한다라고 표현할 수 있다.
예를 들어 회원 가입을 MemberService 클래스에서 구현한다고 해보자.
이 때, 기능이 MemberRepository에 있어 회원 가입 시 MemberRepository의 메서드 save()가 필요하다면,
MemberService 클래스는 MemberRepository 클래스에 의존한다고 표현할 수 있다.
class MemberService{
// MemberService는 MemberRepository에 의존한다.
private MemberRepository memberRepository;
void join(Member member){
memberRepository.save(member);
}
}
DI는 IoC의 대표적인 기능으로 의존하는 객체를 직접 생성하는 대신, 의존 객체를 전달받는 방식을 사용한다.
의존관계주입을 할 때엔 DIP, OCP 원칙에 위배하지 않도록 인터페이스(추상 클래스)에 의존하도록 해야 한다.
의존관계주입은 @Autowired 어노테이션을 이용하며, 의존관계주입하는 방법에는 크게 세 가지가 있다.
1. 생성자 주입
2. 수정자(setter) 주입
3. 필드 주입
이 중 생성자 주입 기본적으로 쓰는 것을 추천한다.
의존관계주입에 대한 더 자세한 내용은 다음 포스트에서 다룰 예정이다.
⛅️ ApplicationContext : 스프링 컨테이너란?
Spring framework에서는 스프링 컨테이너를 통해 객체(Bean)들을 관리한다.
여기서 스프링 컨테이너가 ApplicationContext이다.
● BeanFactory와 ApplicationContext
BeanFactory는 스프링 컨테이너의 최상위 인터페이스이며, 스프링 빈을 관리하고 조회하는 역할을 담당한다.
.getBean()과 같은 기능은 BeanFactory가 지원하는 기능이다.
ApplicationContext는 BeanFactory 기능을 모두 상속받아서 제공한다.
스프링 빈을 관리&조회하는 기능 외에도 앱 개발에 필요한 편리한 부가 기능들을 제공한다.
BeanFactory는 거의 사용하지 않고, ApplicationContext를 사용하기 때문에 ApplicationContext를 스프링 컨테이너라 한다.
자바 설정 클래스를 기반으로 스프링 컨테이너를 만들 수 있다.
new AnnotationConfigApplicationContext(AppConfig.class) -> ApplicationContext 인터페이스의 구현체
싱글톤 패턴이란 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴인데,
스프링 컨테이너는 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
빈(객체)를 스프링 컨테이너에 등록하고, 빈 조회 요청 시 새로 생성하지 않고 스프링 컨테이너에서 빈을 찾아서 반환한다.
⛅️ 스프링 컨테이너의 생성과 빈 등록 과정
1. 스프링 컨테이너 생성
// 스프링 컨테이너 생성 - 구성 정보로 AppConfig.class로 설정
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
위 코드를 통해 스프링 컨테이너를 생성할 수 있다. 이 때 매개변수로 구성 정보를 전달해야 한다.
스프링 컨테이너를 생성한 후, 구성 정보(AppConfig.class)를 활용한다.
2. 스프링 빈 등록
AppConfig.class에서 등록했던 빈 정보를 참고하여 스프링 컨테이너에 저장한다.
스프링 컨테이너는 <key:빈 이름, value:빈 객체> 형태로 빈을 저장한다.
key(빈 이름)은 메서드의 이름으로 사용하고, return 하는 실제 객체를 value(빈 객체)에 저장한다.
3. 스프링 빈 의존관계 설정
스프링 컨테이너는 설정 정보(AppConfig.class)를 참고해서 의존 관계 주입(DI)를 한다.
⛅️ @Configuration과 싱글톤 컨테이너
스프링 컨테이너는 객체들을 어떻게 싱글톤으로 관리할 수 있을까?
정답은 @Configuration에 있다.
스프링은 @Configuration이 붙은 클래스를 설정 정보로 사용한다. AppConfig라는 클래스에 @Configuration을 추가했다고 가정하자.
스프링은 CGLIB이라는 바이트코드를 조작하는 라이브러리를 사용해서 AppConfig 클래스를 상속받은 다른 클래스(AppConfig@CGLIB)를 생성한다.
그리고 이 클래스(AppConfig@CGLIB)를 스프링 빈에 등록한다. 그리고 이 클래스는 빈들을 싱글톤이 되도록 보장해준다.
스프링 컨테이너에서 AppConfig를 .getBean()을 통해 꺼내보면
class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
와 같이 CGLIB에 의해 바뀐 것을 볼 수 있다.
만일 AppConfig에 @Configuration을 뺀다면 빈들이 싱글톤으로 관리되지 않는 것을 볼 수 있다.