2022년 04월 06일 TIL

DI

Dependency Resoloution Process

  • IoC 컨테이너(ApplicationContext)가 configuration metadata에 의해 만들어진다.
  • Bean을 생성하며 해당 Bean에 필요한 의존성(Bean)을 제공한다.

Cicular dependencies

  • 순환 참조
  • A → B, B → A 형태로 참조가 이루어진다.
    • A가 생성되기 위해 B가 필요하지만 B가 생성되기 위해 A도 필요하다.
    • BeanCurrentCreationException 발생

Component scan

스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능

Stereotype Annotation

  • @Component
  • @Repository
  • @Service
  • @Controller
  • @Configuration

@Componentscan

현재 패키지를 기준으로 component scan을 한다.

  • basePackages

    • 스캔 대상 패키지들의 경로를 하드 코딩한다.
    • @ComponentScan(basePackages = {"org.prgms.kdt.order", "org.prgms.kdt.voucher"})
  • basePackageClasses

    • 컴포넌트 스캔을 위해 기준이 되는 클래스들을 설정
    • @ComponentScan(basePackageClasses = {Order.class, Voucher.class})
  • excludeFilters

@Autowired

  • 필드 의존성 주입
@Autowired
private VoucherRepository voucherRepository;
  • setter 주입
private VoucherRepository voucherRepository;

public void setVoucherRepository(VoucherRepository voucherRepository) {
  this.voucherRepository = voucherRepository;
}
  • 생성자 의존성 주입
    • 생성자가 1개일 경우 자동으로 @Autowired가 설정된다.
    • 생성자가 2개일 경우 수동으로 @Autowired가 설정해야 한다.
    • 가장 추천 되는 방법이다.
      • 초기화시에 필요한 모든 의존관계가 형성되기 때문에 안전하다.
      • 잘못된 패턴을 찾을 수 있게 도와준다.
      • 테스트를 쉽게 할 수 있도록 해준다. → mocking을 용이하게 한다.
      • final 키워드를 이용하여 불변성을 확보할 수 있다. → thread safe하다.

@Primary, @Qualifier

@Primary

interface VoucherRepository { ... }

@Repository
@Primary
class MemoryVoucherRepository implements VoucherRepository { ... }

@Repository
class JdbcVoucherRepository implements VoucherRepository { ... }

자동 주입 가능한 Bean이 2개 이상인 경우 주입 우선순위를 설정하기 위해 사용한다.

@Qualifier

interface VoucherRepository { ... }

@Repository
@Qualifier("memory")
class MemoryVoucherRepository implements VoucherRepository { ... }

@Repository
@Qualifier("jdbc")
class JdbcVoucherRepository implements VoucherRepository { ... }

@Service
class VoucherService {
	...
	public Voucher Service(@Qualifier("memory") VoucherRepository voucherRepository){ ... }
    ...
}
  • Bean의 사용 용도를 설정하고 DI시 용도에 맞는 Bean을 주입한다.
  • 특정 qualified된 Bean의 타입 가지고 오기
    VoucherRepository voucherRepository = BasicFactoryAnnotationUtils.qualifiedBeanOfType(
    applicationContext.getBeanFactory(), VoucherRepository.class, "memory");
  • 주입 받는 대상이 어떠한 Bean을 사용할지 걱정하지 않게 하는게 좋다.
    • @Primary가 @Qualifier보다 좋은 방법이다.
    • 하지만 다수의 템플릿이 있고 접속 대상에 따라 서로 다른 템플릿을 사용하는 경우 @Qualifier를 사용한다.

복수 개의 Bean 설정

  • 스프링의 경우 설정할 내용이 많아 파일들을 분리해서 사용한다.

Bean Scope

  • singleton: Bean을 오로지 하나만 생성(기본)
@Repository
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MemoryVoucherRepository implements VoucherRepository { ... }
  • prototype: 매번 새로운 Bean을 생성
@Repository
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MemoryVoucherRepository implements VoucherRepository { ... }

Bean LifeCycle Callbacks

applicationContext.close() → IoC 컨테이너 소멸 → 모든 Bean들 소멸 → Callback 발생

  • Bean 생성 생명주기 콜백
    1) @PostConstruct 적용된 메서드 실행
    2) InitializingBean 구현시 afterPropertiesSet 메서드 호출
    3) @Bean 어노테이션의 initMethod에 설정한 메서드 호출
    • @Bean(initMethod = "init") → init 메서드 호출
  • Bean 소멸 생명주기 콜백
    1) @PreDestroy 적용된 메서드 실행
    2) DisposableBean 구현시 destroy 호출
    3) @Bean 어노테이션의 destroyMethod에 설정한 메서드 호출
    • @Bean(destroyMethod = "destroy") → destroy 메서드 호출

왜 IoC 컨테이너에서 Bean을 관리하나?

  • Bean을 싱글톤으로 관리하기 위해서이다.
    • Bean을 싱글톤을 관리하지 않을 경우 기존에 사용하던 Bean과 다른 Bean을 사용할 수 있기에 항상 같은 참조를 유지하기 위해서 싱글톤으로 관리한다.
    • 추가)

      그 이유는 스프링이 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버환경이기 때문이다. 스프링이 처음 설계되었던 대규모 엔터프라이즈 서버환경은 초당 최대 수십, 수백 개의 요청을 받아 처리하는 높은 성능이 요구되는 환경이었다. 이 때마다 매번 새로운 오브젝트를 생성해서 사용할 경우 아무리 GC의 성능이 좋아졌다고 해도, 서버가 감당하기 힘든 부하가 걸릴 것이다.
      Bean이란? Bean Scope와 싱글톤 레지스트리, Bean 생성방식

좋은 웹페이지 즐겨찾기