(W3D1~W3D3) SpringBoot - DI, IoC

의존성

어떤 객체가 협력하기 위해 다른 객체를 필요로 하는 상태를 의미한다.

의존성은 실행 시점과 구현 시점에 서로 다른 의미를 갖는다.

  • 컴파일타임 의존성 : 코드를 작성하는 시점에서 발생하는 의존성. 클래스 사이의 의존성
  • 런타임 의존성 : 애플리케이션이 실행되는 시점의 의존성. 객체 사이의 의존성

제어의 역전(Inversion of Control)

프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 의미한다.

라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어하지만 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다.

프레임워크가 흐름을 주도하면서 개발자가 만든 애플리케이션 코드를 사용하는 것이다.

IoC 컨테이너

개별 객체들의 의존관계 설정이 자동으로 이루어지고 객체들의 생성과 파괴 조합 등을 관장한다.

객체를 생성하고 관리하면서 의존관계를 연결해준다.

-> 스프링에서는 ApplicationContext 라는 인터페이스로 제공한다,

bean : IoC 컨테이너에서 관리되어지는 객체를 말한다.

configuration metadata : 실제 빈 정보를 configuration metadata로부터 받아온다.

ApplicationContext 인터페이스의 구현체

  • GenericXmlApplicationContext : XML 기반
  • AnnotationConfigApplicationContext : Java 기반

의존성 주입(Dependency Injection)

객체 사이의 의존 관계를 스프링 설정 파일에 등록된 정보를 바탕으로 IoC 컨테이너가 자동으로 처리하는 것을 의미한다.

IoC를 다양한 방법으로 만들 수 있다. - 전략패턴, 서비스 로케이터 패턴, 팩토리 패턴, 의존관계 주입패턴 등이 있다.

생성자를 통해서 주입을 받는 패턴을 생성자 주입 패턴이라고 한다

setter기반, 필드 기반(Autowired) 의존관계 주입 방법도 있다.

스프링 IoC 컨테이너에서 빈이 잘못 등록될 수 있다.

Circular dependencies : A -> B 참조 / B -> A 참조할 경우 순환 의존관계가 형성되면서 BeanCurrentlyInCreationException 예외가 발생할 수 있다.

컴포넌트 스캔으로 빈 등록하기

스프링이 직접 클래스를 검색해서 자동으로 빈을 등록한다.

@Configuration 클래스에서 빈을 등록 안해도 된다.

stereotype 어노테이션을 이용하여 찾는다.

스프링에서 여러 개의 stereotype 어노테이션으로 용도에 맞게 분류했다. 빈들을 모두 동일시하게 생각하지 않고 용도에 맞게 분류시킨 것이다.

컴포넌트 스캔은 @Configuration 클래스 파일이 있는 동일한 패키지 내에서 수행된다.

어노테이션은 인터페이스가 아닌 구현클래스에 붙인다.

컴포넌트 스캔 제외 대상을 지정할 수 있다.

  1. 베이스 패키지 설정, basePackagesClasses-클래스가 속한 패키지 기준
  2. excludeFilters @Component.Filter(type = Filter.ASSIGNABLE_TYPE, value = MemoryRepo.class)

    • Filter.ASSIGNABLE_TYPE : value 로 지정된 빈을 제외할 수 있다.

@Autowired을 이용한 의존관계 자동 주입

@Autowired 어노테이션을 사용하면 IoC 컨테이너가 자동으로 주입한다.

필드에 적용할 수 있고, setter에 적용할 수도 있다.

생성자가 두 개일 경우에는 스프링에 자동으로 주입이 되는 생성자에 @Autowired를 달아주어야 한다.

생성자 기반 의존관계 주입을 선택해야 하는 이유

  • 초기화시에 필요한 모든 의존관계가 형성되기 때문에 안전하다. (객체 생성시에 필요한 모든 의존관계를 주입받았다고 확신할 수 있다.)
  • 잘못된 패턴을 찾을 수 있게 도와준다. (많은 파라미터를 가지고 있으면 많은 책임을 가지고 있다는 것을 암시한다.->관심사 분리가 필요하다.)
  • 테스트를 쉽게 해준다. (쉽게 목 객체를 전달할 수 있다. 스프링 도움없이)
  • 불변성을 확보한다. (final 키워드를 사용할 수 있다. 한 번 만들어진 의존관계가 변경안되게 도와준다. - thread safety 보장)

@Primary @Qualifer 어노테이션을 이용한 의존관계 설정

자동 주입이 가능한 빈이 2개 이상이면 어떤 빈이 주입되어야 하는지 선택해야한다. 스프링은 무슨 객체를 주입해야하는지 모르기 때문에 에러가 발생한다.

-> 우선시 되는 빈에 @Primary 달아준다.

-> 등급이 같지만 다른 용도면 @Qualifier 달아준다. 사용하는 쪽에서 둘 중에 무엇을 쓸지 @Qualifer로 선택한다.

getBean()을 할 경우 Qualifer 전달할 방법이 없다.

  • 빈의 이름을 다르게 하거나
  • BeanFactoryAnnotationUtils를 이용해서 해결한다.

    • 특정 Qualified된 빈의 타입을 가져온다. 컨테이너에서 객체를 빼올 수 있다.

대체로 @Qualifier 보다는 @Primary를 이용한다. 동일한 객체를 만들지 않는다. -> 쓰는 쪽에서 고민하지 않게 하는게 제일 좋다.

@SpringBootApplication : 컴포넌트 스캔을 포함하고 있다.

Bean Scope

Bean이 어떤 범위로 만들어지는지를 의미한다. 기본적으로 싱글톤 스코프를 가진다.

Bean definition을 작성하면 스프링에게 어떻게 객체를 만들어야하는지 알려준다. 하나의 Bean definition에 의해 여러 객체가 만들어질 수 있고 단 하나만 만들어질 수 있다. 단 하나만 만들어지는 것을 싱글톤 스코프라고 한다.

  • 싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.
  • 프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다.

싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환한다.

프로토타입 스코프의 빈을 스프링 컨테이너에 조회하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성해서 반환한다.

Bean Lifecycle

스프링 컨테이너는 객체 생성과 소멸 - 생명주기를 관리한다.

스프링 컨테이너 조차도 생명주기를 가진다.

ApplicationContext.close() 소멸 : 컨테이너에 등록된 모든 빈이 소멸된다. -> 소멸에 대한 콜백들이 동작한다.

  • 빈 생성 생명주기 콜백

    • @PostConstruct 어노테이션이 적용된 메소드 호출
    • Bean이 InitializingBean 인터페이스 구현시 afterPropertiesSet 호출
    • @Bean 어노테이션의 initMethod에 설정한 메소드 호출
  • 빈 소멸 생명주기 콜백

    • @PreDestroy 어노테이션이 적용된 메소드 호출
    • Bean이 DisposableBean 인터페이스 구현시 destroy 호출
    • @Bean 어노테이션의 destroyMethod에 설정한 메소드 호출

Written by@Myunghwan
Nothing changes if nothing changes

GitHub