Spring 이해하기

이재용·2025년 1월 1일

의존관계

class A {
	B b = new B();
}

A가 B를 사용
"의존대상 B가 변하면, 그것이 A에 영향을 미친다."
B의 기능이 추가 또는 변경될 때, A에 영향을 미치는 경우 A가 B에 의존한다.

DI (Dependency Injection)

객체가 의존객체를 직접 생성해서 사용하는게 아니라, 의존관계를 객체 외부에서 결정하고 주입해서 사용하는 것

  • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
  • 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
  • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.

제어의 역전 IoC (Inversion of Control)

프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라 한다. 구현 객체는 자신의 로직을 실행하는 역할만 담당한다. 프로그램의 제어 흐 름은 AppConfig가 가져간다.

생성자 주입

  1. 순환 참조를 방지할 수 있다.
    • 필드주입/setter 주입 -> 객체 생성 후 비즈니스 로직상에서 순환참조가 일어난다. 필드 주입이나, 수정자 주입은 객체 생성 시점에는 순환참조가 일어나는지 아닌지 발견할 수 있는 방법이 없다.
    • 생성자 주입 -> 객체 (빈) 생성 시점에서 순환참조가 일어난다.
  2. final 선언이 가능하다.
    • 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
    • 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.
    • 수정자 주입은 의존관계가 변경될 수 있다.
  3. 테스트하기 용이하다.
    • 필드주입은 DI 프레임워크가 없으면 아무것도 할 수 없다.

순환참조
스프링 컨테이너는 A 클래스의 빈을 생성하기위해 B 클래스의 빈을 주입해줘야 한다. 따라서 B 클래스의 빈을 찾을 것이다. 근데 B 클래스의 빈이 없으니까 B 클래스의 빈을 생성하려 하니 A 클래스의 빈이 필요해서A 클래스의 빈을 생성하려 하고 무한 반복에 빠지게 된다.

스프링 컨테이너

ApplicationContext를 스프링 컨테이너라 한다. @Configuration이 붙은 클래스를 구성 정보로 사용한다. 여기서 @Bean이 붙은 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. (스프링 컨테이너 생성->스프링 빈 등록->의존관계 설정)
스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

스프링 빈

스프링 컨테이너에 등록된 객체.
빈 이름은 메서드 이름.

싱글톤 방식의 주의점
진짜 공유필드는 조심해야 한다! 스프링 빈은 항상 무상태(stateless)로 설계하자.

컴포넌트 스캔&의존관계 자동 주입
@ComponentScan을 설정 클래스에 붙인다. -> @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.
-> 생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.

컴포넌트 스캔 기본 대상

  • @Component : 컴포넌트 스캔에서 사용
  • @Controller : 스프링 MVC 컨트롤러에서 사용
  • @Service : 스프링 비즈니스 로직에서 사용
  • @Repository : 스프링 데이터 접근 계층에서 사용
  • @Configuration : 스프링 설정 정보에서 사용

스프링 빈의 이벤트 라이프사이클

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료

초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출 -> @PostConstruct
소멸전 콜백: 빈이 소멸되기 직전에 호출 -> @PreDestroy

0개의 댓글