Spring Framework
DI - 스프링 컨테이너(Spring Container), 빈(bean)
ChaeHing
2023. 4. 5. 00:03
DI(Dependency Injection)
- 의존성 주입
- 생성자를 통한 의존관계 주입이 스프링에서 공식적으로 추천하는 방법
스프링 컨테이너(Spring Container)
- 스프링 컨테이너는 스프링 프레임워크의 핵심 컴포넌트
- 컴포넌트 : 재사용 가능한 웹의 구성요소(각각의 독립된 모듈)
- 자바 객체(Bean)의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공
- Bean 생성, 관리, 제거 등의 역할 (생명주기)
- 애플리케이션 컨텍스트라고도 한다.
빈(bean)
- 인스턴스화된 객체를 의미
- 스프링은 스프링 컨테이너를 통해 객체를 관리, 스프링 컨테이너에서 관리되는 객체를 빈(Bean)
- bean은 애플리케이션에서 사용하는 객체, 설정 정보와 함께 스프링 컨테이너에 등록된 객체(인스턴스화된 객체)를 의미
BeanDefinition
- 빈(bean) 설정 메타정보
- @Bean 당 각 1개씩 메타 정보가 생성
- 다양한 형태의 설정 정보를 BeanDefinition으로 추상화
- 스프링 컨테이너는 Configuration Metadata를 사용
- 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록
- new AnnotationConfigApplicationContext(구성정보.class)로 스프링에 있는 @Bean의 메서드를 등록
static AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(DenpendencyConfig.class)
스프링 컨테이너의 종류
- BeanFactory
- 스프링 컨테이너의 최상위 인터페이스
- 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리
- ApplicationContext
- BeanFactory의 기능을 상속
- BeaFactory 기능 + 그외 부가기능 제공
- MessageSource: 메세지 다국화를 위한 인터페이스
- EnvironmentCapable: 개발, 운영 등 환경변수 등으로 나눠 처리하고, 애플리케이션 구동 시 필요한 정보들을 관리하기 위한 인터페이스
- ApplicationEventPublisher: 이벤트 관련 기능을 제공하는 인터페이스
- ResourceLoader: 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회
BeanFactory와 ApplicationContext의 차이
BeanFactory
- Lazy(게으른) Loading
- 빈을 실질적으로 사용할때만 로딩 하기 때문에 가벼운 경량 컨테이너 -> 메모리를 적게 차지 한다 (효율성)
- 빈을 사용할때 오류 유무를 확인 (미리 확인 불가)
ApplicationContext
- Eager Loading
- 런타임 실행 시 모든 빈을 로딩 -> 바로 사용안하는 빈들도 로딩하기때문에 메모리를 많이 차지
- 문제가 있는 bean 객체가 있을때 오류의 유무를 미리 확인 (컴파일 단계에서)
- 컨테이너는 런타임 실행 시점에 등록된 모든 빈을 로딩해야하기 때문에 부담이 크다.
- 모든 빈을 생성하기 때문에 런타임이 오래걸린다.
- 간단한 테스트를 해야하는데에도 런타임 시간이 오래걸려 사용이 불편해진다.
- 이러한 문제를 해결하기위해 Lazy Loading을 사용할 수도 있다. -> BeanFactory를 상속받았기 때문에
싱글톤(singleton) 스코프
- 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
- 스프링 컨테이너에서 빈 스코프의 기본값은 싱글톤 스코프이다.
- 여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 무상태로 설계
- 싱글톤 사용시 read only를 의미한다.
public class SingletonService {
// 객체를 static으로 딱 1개만 생성
private static final SingletonService instance = new SingletonService();
// 객체 인스턴스 필요시 해당 public static 메서드를 통해 조회만 가능
public static SingletonService getInstance(){
return instance;
}
// 생성자를 private로 만들어 new를 통해 객체 생성을 막는다.
private SingletonService() {}
}
싱글톤 컨테이너를 통한 생성
public class SingletonTest {
static AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(DenpendencyConfig.class);
static MemberService memberService1 = ac.getBean("memberService", MemberService.class);
static MemberService memberService2 = ac.getBean("memberService", MemberService.class);
public static void main(String[] args) {
System.out.println("memberService1 : " + memberService1);
System.out.println("memberService2 : " + memberService2);
}
@Configuration
public class DenpendencyConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemberRepository();
}
@Bean
public CoffeeService coffeeService() {
return new CoffeeService(coffeeRepository());
}
@Bean
public CoffeeRepository coffeeRepository() {
return new CoffeeRepository();
}
}
컴포넌트 스캔 (@ComponentScan)
- 스프링은 설정 정보 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공
- @bean으로 직접 작성시 설정 정보가 커지고, 누락하는 등 다양한 문제가 발생
- 이러한 문제를 해소 하기 위한 방법
- @ComponentScan - @ComponentScan이 등록된 곳에서 @Component를 가져오기 위해 사용
- @Componenet 애너테이션이 붙은 클래스를 찾아서 스프링 빈으로 등록
- @Configuration 해당 애너테이션에는 @Component가 적용되어있기 때문에 ComponentScan할 때 자동으로 스프링 빈 등록
- @Autowired - 생성자 의존성 주입에 필요한 설정 정보 대신 의존관계 자동 주입을 해주게 된다.
- @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치, 해당 패키지부터 하위 패키지 모두 탐색
- 설정 정보 클래스의 위치를 프로젝트 최상단에 두고 패키지 위치는 지정하지 않는 방법이 가장 편할 수 있다.
package com.example.section2week4;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan
public class AudoDependencyConfig {
}
package com.example.section2week4.coffee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CoffeeService {
private static CoffeeRepository coffeeRepository;
@Autowired
public CoffeeService(CoffeeRepository coffeeRepository) {
this.coffeeRepository = coffeeRepository;
}
public void createCoffee(Coffee coffee) {
coffeeRepository.postCoffee(coffee);
}
public Coffee editCoffee(Long coffeeId, String korName, int price) {
return coffeeRepository.patchCoffee(coffeeId, korName, price);
}
public Coffee getCoffee(Long coffeeId) {
return coffeeRepository.getCoffee(coffeeId);
}
public void deleteCoffee(Long coffeeId) {
coffeeRepository.deleteCoffee(coffeeId);
}
}
package com.example.section2week4.coffee;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class CoffeeRepository {
private static Map<Long, Coffee> drinks = new HashMap<>();
public void postCoffee(Coffee coffee) {
drinks.put(coffee.getCoffeeId(), coffee);
}
public Coffee patchCoffee(Long coffeeId, String korName, int price) {
Coffee drink = drinks.get(coffeeId);
drink.setKorName(korName);
drink.setPrice(price);
return drinks.put(coffeeId, drink);
}
public Coffee getCoffee(Long coffeeId) {
return drinks.get(coffeeId);
}
public void deleteCoffee(Long coffeeId) {
drinks.remove(coffeeId);
}
}
애너테이션 정리
- @Configuration - 해당 객체가 bean definitions의 소스임을 나타내는 애너테이션
- @Bean - @Bean-annoted 메서드를 통해 bean을 선언
- @ComponentScan - 설정 정보 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔
- @Component가 붙은 모든 클래스를 스프링 빈으로 등록
- @Autowired - 의존관계를 자동으로 주입
다양한 의존관계 주입 방법
- 생성자 주입 - 권장되는 방법
- 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입합니다.
- 수정자 주입 (setter 주입)
- 필드 주입
- 일반 메서드 주입
순환참조
- 개발을 하다보면 여러 서비스들 간에 의존관계 발생
- ex) service1 -> service2 의존, service2 -> service 1 의존
- 어느 객체가 생성이 되어야 의존 객체가 생성되는데 그게 불가능 -> 누가 먼저 인지 모른다. 누구를 먼저 생성해야 되는지 모른다.
- 생성자 주입은 빈을 생성할 시점에 바로 주입하기때문에 순화 참조 문제를 인지 할 수 있다.
- 필드 주입, 수정자 주입 -> 나중에 주입하기 때문에 참조 문제 인지 불가 - 이러한 이유로 현재는 사용 하지 않음
- 생성자 주입을 사용하는 가장 큰 이유중 하나
간단 정리 (요약) - 이것들만은 알고가자
- DI는 IoC라는 원칙을 구현하기 위해서 사용되는 방법중 하나로 의존성 주입을 의미한다.
- 스프링 컨테이너(Spring Container)는 빈의 생명주기를 관리한다.
- 빈(bean)은 인스턴스화된 객체를 의미합니다.
- BeanDefinition는 빈(bean) 설정 메타정보
- 스프링컨테이너에는 BeanFactory와 ApplicationContext 두개가 있다.
- ApplicationContext가 스프링 컨테이너
- @Configuration이 붙은 DependencyConfig를 설정 정보로 사용
- @Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록
- 스프링은 설정 정보 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔(Component Scan)이라는 기능을 제공
- @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록해주기 때문에 설정 정보에 붙여주면 된다.
- 의존관계도 자동으로 주입하는 @Autowired 기능도 제공