Spring DI(Dependency Injection)

InSeok·2022년 9월 9일
0

TIL

목록 보기
27/51

목차


  1. 스프링 컨테이너
  2. Bean
  3. 의존관계 주입 DI
  4. 컴포넌트 스캔

배운 내용


  • AppConfig - 애플리케이션의 전체 동작을 구성하기 위해 사용
  • 직접 객체를 생성하는 코드에서 생성자를 통해 객체를 주입 받고 해당 객체는 AppConfig에 의해 결정될 수 있도록 코드를 수정
  • 서비스Impl는 의존관계에 대해 신경 쓸 필요 없이 실행에만 집중
  • 구현 부분에 변경사항이 생길 경우, AppConfig에서 구현 부분만 수정

**스프링 컨테이너(Spring Container)**

  • 컨테이너 : 내부에 또 다른 컴포넌트를 가지고 있는 어떤 컴포넌트
    • 객체를 생성하고 객체를 서로 연결
    • 객체의 의존성을 확인해 생성한 뒤 적절한 객체에 의존성을 주입
  • 스프링 프레임워크의 핵심 컴포넌트
  • 내부에 존재하는 애플리케이션 빈의 생명주기를 관리
    • Bean 생성, 관리, 제거 등의 역할 담당
  • ApplicationContext를 스프링 컨테이너라고 하고 인터페이스로 구현되어있다
  • @Configuration : 구성정보를 담당하는것을 설정할때 @Configuration 을 붙여준다.
  • @Bean : 각 메서드에 @Bean을 붙이면 스프링 컨테이너에 자동으로 등록
  • 컨테이너는 개발자가 정의한 Bean을 객체로 만들어 관리하고 개발자가 필요로 할 때 제공
  • 의존성 주입을 통해 애플리케이션의 컴포넌트를 관리
    • 서로 다른 빈을 연결해 애플리케이션의 빈을 연결하는 역할
  • 스프링 컨테이너를 만드는 다양한 방법은 ApplicationContext 인터페이스의 구현체이다.
  • 스프링 컨테이너는 Configuration Metadata를사용
    • 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록
    • new AnnotationConfigApplicationContext(구성정보.class)로 스프링에 있는 @Bean의 메서드를 등록 → 애너테이션 기반 컨테이너 구성
    • 어떤 객체가 주입될지는 외부 (AppConfig)에서 결정
    • 애플리케이션 클래스는 구성 메타데이터와 결합되어 ApplicationContext 생성 및 초기화된 후 완전히 구성되고 실행 가능한 시스템 또는 애플리케이션을 갖게 된다.

스프링 컨테이너 **종류**

**BeanFactory**

  • 스프링 컨테이너의 최상위 인터페이스
  • 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할
  • @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용해 빈 등록

**ApplicationContext**

  • BeanFactory의 기능을 상속 + 부가기능

**빈(Bean)**

  • 스프링 컨테이너에 의해 관리되는 재사용 소프트웨어 컴포넌트
  • 빈(bean)은 인스턴스화된 객체이다.
  • @Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록
  • 빈은 클래스의 등록정보, 게터/세터 메서드를 포함
  • 빈은 컨테이너에 사용되는 설정 메타데이터로 생성
  • 설정 메타데이터
    • XML 또는 자바 애너테이션, 자바 코드로 표현
    • 컨테이너의 명령과 인스턴스화, 설정, 조립할 객체를 정의
  • ApplicationContext 사용하여 bean 정의를 읽고 액세스가능
    • getBean 을 사용하여 bean의 인스턴스를 가져올 수 있다.
    • 실제적으로 응용 프로그램 코드에서는 getBean() 메서드로 호출하여 사용하면 안됨

**BeanDefinition(빈 설정 메타정보)**

  • 속성에 따라 컨테이너가 Bean을 어떻게 생성하고 관리할지 결정
  • 스프링이 설정 메타정보를 BeanDefinition 인터페이스를 통해 관리
  • @Bean당 각 1개씩 메타 정보가 생성되고, 스프링 컨테이너는 이런 메타 정보를 기반으로 스프링 빈을 생성
  • 스프링 컨테이너는 설정 형식이 XML인지 Java 코드인지 모르고 BeanDefinition만 알면 된다

BeandDefiniton 구성

  • BeanClassName : 생성할 빈의 클래스 명(자바 설정처럼 팩토리 역할의 빈을 사용하면 없음)
  • factoryBeanName : 팩토리 역할의 빈을 사용할 경우 이름, 예) appConfig
  • factoryMethodName : 빈을 생성할 팩토리 메서드 지정, 예) userService
  • Scope : 싱글톤(기본값)
  • lazyInit : 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때까지 최대한 생성을 지연처리 하는지 여부
  • InitMethodName : 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
  • DestoryMethodName : 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
  • Constructor arguments, Properties : 의존관계 주입에서 사용한다. (자바 설정처럼 팩터리 역할의 빈을 사용하면 없음)

**빈 스코프(Bean Scope)**

  • 특정 bean 정의에서 생성된 개체에 연결할 다양한 의존성 및 구성 값뿐만 아니라 특정 bean 정의에서 생성된 개체의 범위도 제어할 수 있다.

**싱글톤(singleton) 스코프**

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는디자인 패턴
  • 스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때 까지 유지
  • 단일 인스턴스는 싱글톤 빈의 캐시에 저장된다.
  • 이름이 정해진 빈에 대한 모든 요청과 참조는 캐시된 개체를 반환
    • 싱글톤 스코프의 스프링 빈은 여러번 호출해도 모두 같은 인스턴스 참조 주소값을 가진다.
  • 스프링 컨테이너에서 빈 스코프의 기본값은 싱글톤 스코프이다.
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.
  • 싱글톤 객체로 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
  • 싱글톤 방식은 여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 무상태로 설계해야 한다.
    • 특정 클라이언트가 값을 변경할 수 있으면 안된다.
    • 읽기만 가능해야 한다.
    • 스프링 빈의 공유 값을 설정하면 장애가 발생할 수 밖에 없다.

**AnnotationConfigApplicationContext 를 사용하여 스프링 컨테이너를 인스턴스화**

  • @Configuration 클래스가 입력으로 제공되면 @Configuration 클래스 자체가 Bean 정의로 등록되고 클래스 내에서 선언된 모든 @Bean 메서드도 Bean 정의로 등록
  • @Component 클래스JSR-330 클래스가 제공되면 빈 정의로 등록되며 필요한 경우 해당 클래스 내에서 @Autowired 또는 @Inject와 같은 DI 메타데이터가 사용되는 것으로 가정

**@Bean**

  • 메서드 레벨 애너테이션

**빈 선언**

  • @Bean 애너테이션을 메서드에 추가해서 Bean으로 정의(선언)

**빈 의존성**

  • @Bean 애너테이션이 추가된 메서드는 빈을 구축하는데 필요한 의존성을 나타내는데 매개 변수를 사용가능

**@Configuration**

  • 해당 객체가 bean definitions의 소스임을 나타내는 애너테이션
  • @Bean-annoted 메서드를 통해 bean을 선언
  • @Bean 메서드에 대한 호출을 사용하여 bean 사이의 의존성을 정의

**Bean 사이에 의존성 주입**

  • 빈이 서로 의존성을 가질 때, 다른 bean 메서드를 호출하여 나타냄

Bean객체 설정 과정

  1. AppConfig.java 등의 설정 정보에 작성한 @Bean을 통해 스프링 컨테이너에 빈 객체를 저장
  • 빈 객체에 이름은 메서드 이름을 사용해야 한다.
    • 직접 부여도 가능
      ex) @Bean(name="UserRepository")
    • 이름의 경우엔 항상 다른 이름을 부여해야 한다.
      • 같은 이름을 부여하면 다른 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류가 발생
  1. 빈 객체가 생성되면 빈 의존관계를 설정

**다양한 의존관계 주입 방법**

**생성자 주입**

  • 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입
  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장
  • 생성자가 1개만 존재하는 경우에는 @Autowired를 생략해도 자동 주입됨

**수정자 주입 (setter 주입)**

  • set필드명 메서드를 생성하여 의존 관계를 주입
  • @Autowired를 입력하지 않으면 실행이되지 않음

**필드 주입**

  • 필드에 @Autowired 붙여서 바로 주입
  • 외부에서 변경이 불가능하여 테스트하기 힘들다

**옵션 처리**

  • 주입할 스프링 빈이 없을 때 동작해야하는 경우
  • @Autowired만 사용하는 경우 required 옵션 기본값인 true가 사용되어 자동 주입 대상이 없으면 오류가 발생하는 경우가 있음
  • 스프링 빈을 옵셔널하게 해둔 상태에서 등록이 되지 않고, 기본 로직으로 동작하게 하는 경우
  • @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출되지 않음
  • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력됩니다.
  • Optional<> : 자동 주입할 대상이 없으면 Optional.empty가 입력됩니다.

**생성자 주입을 사용해야하는 이유**

  • 불변
    • 의존 관계 주입은 처음 애플리케이션이 실행될 때 대부분 정해지고 종료 전까지 변경되지 않고 변경되서는 안됩니다.
    • 수정자 주입 같은 경우에는 이름 메서드를 public으로 열어두어 변경이 가능하기 때문에 적합하지 않음.
  • final 키워드 사용 가능
    • 생성자에서 값이 설정되지 않으면 컴파일 시점에서 오류를 확인
  • 순환 참조 방지
    • 순환 참조 시 앱구동이 실패
  • 의존관계 설정이 되지 않으면 객체생성이 불가능
    • 컴파일 타임에 인지가 가능
    • NPE 에러 방지
  • 테스트 코드 작성 용이

**Component 스캔**

  • 설정 정보 없이 자동으로 스프링 빈을 등록하는 기능
  • @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록해주기 때문에 설정 정보에 붙여주면 된다.
  • 의존관계도 자동으로 주입하는 @Autowired 기능도 제공
  • 컴포넌트 스캔을 사용하면 @Configuration이 붙은 설정 정보도 자동으로 등록
  • @Component - @ComponentScan이 등록된 곳에서 @Component를 가져오기 위해 사용됩니다.
  • @Autowired - 생성자 의존성 주입에 필요한 설정 정보 대신 의존관계 자동 주입

**basePackages**

  • 탐색할 패키지의 시작 위치를 지정하고, 해당 패키지부터 하위 패키지 모두 탐색
  • @ComponentScan()의 매개변수로 basePackages = “”를 사용가능
  • 지정하지 않으면, @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
    • 설정 정보 클래스의 위치를 프로젝트 최상단에 두고 패키지 위치는 지정하지 않는 방법이 가장 편함
  • 스프링 부트를 사용하면 @SpringBootApplication를 이 프로젝트 시작 루트 위치에 두는 것을 추천
    • @SpringBootApplication@ComponentScan 포함

**컴포넌트 스캔 기본 대상**

  • @Component : 컴포넌트 스캔에서 사용됩니다.
  • @Controller & @RestController : 스프링 MVC 및 REST 전용 컨트롤러에서 사용됩니다.
  • @Service : 스프링 비즈니스 로직에서 사용됩니다.
  • @Repository : 스프링 데이터 접근 계층에서 사용됩니다.
    • 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
  • @Configuration : 스프링 설정 정보에서 사용됩니다.
    • 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.

**필터**

  • includeFilters : 컴포넌트 스캔 대상을 추가로 지정합니다.
  • excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정합니다.
  • FilterType 옵션
    • ANNOTATION: 기본값, 애너테이션으로 인식해서 동작합니다.
    • ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작합니다.
    • ASPECTJ: AspectJ 패턴을 사용합니다.
    • REGEX: 정규 표현식을 나타냅니다.
    • CUSTOM: TypeFilter라는 인터페이스를 구현해서 처리합니다.
  • ex) @ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
profile
백엔드 개발자

0개의 댓글