스프링 컨테이너와 빈
개념
스프링 컨테이너는 스프링 프레임워크의 핵심 컴포넌트임.
스프링 컨테이너는 자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공함.
ApplicationContext(인터페이스)를 스프링 컨테이너라고 함.
AnnotationConfigApplicationContext가 구현체임.
스프링에서는 자바 객체를 빈(Bean)이라 함.
생성과정
-
스프링 컨테이너의 생성
-
스프링 컨테이너 생성 시 구성 정보 지정해야 함. ex) AppConfig.class
-
스프링 빈 등록
-
스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용하여 스프링 빈을 등록함.
-
빈 이름은 메서드 이름을 사용.
-
빈 이름 직접 부여 가능(보통 메서드 이름 그대로 사용)
* 빈 이름은 중복되게 만들지 않아야 함!
-
스프링 빈 의존관계 설정
-
스프링 컨테이너는 설정 정보를 참고하여 의존관계 주입(DI)
-
스프링 컨테이너는 싱글톤 패턴 보장
스프링 빈 조회
-
기본 조회 방법
-
ac.getBean(beanName, type)
-
ac.getBean(type)
조회 대상 없으면 NoSuchBeanDefinitionException 발생
-
동일한 타입 둘 이상
-
상속 관계
-
부모 타입으로 조회하면 자식 타입도 함께 조회
-
따라서 Object 타입으로 조회하면, 모든 스프링 빈을 조회함
싱글톤 컨테이너
웹 애플리케이션과 싱글톤
순수 자바 코드로 만든 DI컨테이너의 문제점
: 요청을 할 때마다 객체를 새로 생성하여 메모리 낭비가 심해짐
해결책1 : 해당 객체가 한 번만 생성되는 싱글톤 패턴을 활용하기
-
static 영역에 객체 딱 1 개만 생성해둠.
private static final Singleton instance = new Singleton();
-
public으로 열어서 static 메서드를 통해서만 조회하도록 허용
public staic Singleton getInstance() {
return instance;
}
-
생성자를 private으로 선언해서 외부에서 객체 생성 할 수 없게 막음
private Singleton(){}
문제점
-
싱글톤 패턴 구현 코드 자체가 많이 들어감
-
DIP를 위반함(클라이언트가 구체 클래스에 의존)
-
OCP원칙을 위반할 가능성이 높아짐
-
getInstance()를 계속 사용해야해서 테스트하기 어려워짐
-
즉, 유연성이 떨어짐
해결책2 : 앞서 살펴본 싱글톤 컨테이너 사용하기
스프링 컨테이너는 싱클톤 패턴의 문제점을 해결하며, 객체 인스턴스를 싱글톤으로 관리함.
-
스프링 컨테이너는 싱글톤 컨테이너 역할을 함
-
싱글톤 패턴을 구현할 때 사용한 지저분한 코드들이 필요 없음
-
DIP, OCP, 테스트, private 생성자로부터 자유로움
주의점
-
하나의 같은 객체 인스턴스를 공유하기에 무상태하게 설계해야함
-
특정 클라이언트에 의존적인 필드가 없어야함
-
특정 클라이언트가 값을 변경할 수 없어야 함
싱글톤이 보장되는 이유
@Configuration은 설정파일을 만들기 위한 애노테이션 or Bean을 등록하기 위한 애노테이션으로 이것이 구성정보@CGLIB로 만들고 싱글톤 패턴이 적용되도록 한다.
*따라서 @Configuration을 적용하지 않고 @Bean만 적용하면 싱글톤이 보장되지 않는다.
컴포넌트 스캔
컴포넌트 스캔과 의존관계 자동 주입
- @Componentscan 은 @Component어노테이션 및 streotype(@Service, @Repository, @Controller, @Repository, @Configuration)어노테이션이 부여된 Class들을 자동으로 Scan하여 Bean으로 등록해주는 역할을 하는 어노테이션임
-
@Autowired는 필요한 의존 객체의 “타입"에 해당하는 빈을 찾아 자동으로 주입해주는 것
탐색 위치와 기본 스캔 대상
중복 등록과 충돌
-
자동 빈 등록 vs 자동 빈 등록
-
컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데 스프링은 이에 대해 오류 발생시킴
ConflictingBeanDefinitionException 예외 발생
-
수동 빈 등록 vs 자동 빈 등록
-
수동 빈 등록이 우선권을 가짐
* 스프링 기능 대부분은 단순한 것보다 복잡한 것이 우선권을 가짐
다양한 의존관계 주입 방법(@Autowired)
-
생성자 주입
-
생성자를 통해서 의존 관계 주입
-
생성자 호출시점에 딱 1 번만 호출되는 것 보장
why? 생성자는 한 번만 호출되기 때문
-
불변, 필수 의존관계에 사용
-
생성자 주입 시 final 키워드 꼭 붙이기
* 생성자가 딱 1 개만 있으면 @Autowired 생략 가능(자주 사용하니 꼭 기억하기)
@Component
public class XXXServiceImpl implements XXXService{
private final XXXRepository XXXrepository;
@Autowired //생성자 한 개여서 생략 가능
public XXXServiceImpl(XXXRepository XXXrepository) {
this.XXXrepository = XXXrepository;
}
}
-
수정자 주입(setter 주입)
-
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해 의존 관계 주입
-
선택, 변경 가능성이 있는 의존 관계에 사용
-
자바빈 프로퍼티 규약의 수정자 메서드 방식 사용
* @Autowired는 주입할 대상이 없으면 오류 발생
(주입 대상 없이 오류발생하지 않는 법 뒤에서 다룸)
@Component
public class XXXServiceImpl implements XXXService{
private final XXXRepository XXXrepository;
@Autowired
public void setXXXRepository(XXXRepository XXXrepository) {
this.XXXrepository = XXXrepository;
}
}
-
필드 주입
-
이름 그대로 필드에 바로 주입하는 방법
-
코드가 간결하지만 외부 변경이 불가능해 테스트하기 힘듦
-
사용하지 않을 것
@Component
public class XXXServiceImpl implements XXXService{
@Autowired
private final XXXRepository XXXrepository;
}
주의 : 순수한 자바 테스트 코드에는 @Autowired동작 안함
-
일반 메서드 주입
-
일반 메서드를 통해 주입 받을 수 있음
-
한 번에 여러 필드 주입 받을 수 있음
-
사용 그냥 거의 안함
옵션 처리
주입할 스프링 빈이 없어도 동작해야할 때 해결책
-
@Autowired(required=false) : 자동 주입할 대상 없으면 수정자 메서드 자체가 호출 안됨
-
@Nullable : 자동 주입할 대상이 없으면 null이 입력
-
Optional<> : 자동 주입할 대상이 없으면 Optional.empty가 입력
생성자 주입 사용하기
최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장함
이유
불변
-
대부분의 의존관계 주입은 한 번 일어나면 변경할 일 없음
-
수정자 주입을 사용하면, setter를 public으로 열어두어야 함
-
변경하면 안되는 메서드를 열어두는 것은 좋은 설계방법이 아님(데이터 은닉)
-
-> 생성자 주입은 객체를 생성할 때 딱 1 번 호출되니 불변 설계 가능
누락
-
생성자 주입을 사용하면 주입 데이터 누락 시 final 키워드에 의해 컴파일 오류 발생
-
최고의 오류는 컴파일 오류임
조회 빈이 2 개 이상일 때
@Autowired는 type으로 조회하기에 같은 타입이 여러 개 존재하여 오류가 발생할 수 있음
NoUniqueBeanDefinitionException오류 발생
해결책
-
@Autowired 필드 명 매칭
-
@Autowiredsms 타입 매칭을 시도하고, 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭함.
-
타입 매칭을 시도하고 그 결과에 여러 번이 있을 때 추가로 동작하는 것
-
@Qualifier 사용
@Qualifier는 추가 구분자를 붙여주는 방법
-
빈 등록시 @Qualifier를 붙임
@Component
@Qualifier("aaaXXXServicePolicy")
public class XXXServicePolicy implements XXXPolicy {}
-
주입 시 @Qualifier를 붙여주고 등록한 이름 적어줌
@Autowired
public XXXServiceImpl(@Qualifier("aaaXXXServicePolicy")
XXXPolicy xxxPolicy) {
this.xxxPolicy = xxxPolicy;
}
-
만약 이 경우에 aaXXXServicePolicy을 찾지 못하면 aaXXXServicePolicy이름의 스프링 빈을 추가로 찾음
-
@Primary 사용(자주 사용함)
@Primary는 우선 순위를 벙하는 방법으로 여러 빈이 매칭되면 @Primary가 우선권을 가짐
@Component
@Primary
public class AAAPolicy implements Policy{}
@Component
public class BBBPolicy implements Policy{}
-
이 경우에 오류가 발생하지 않고 AAAPolicy가 우선권을 갖고 선택됨
* @Primary vs @Qualifier
빈 생명주기 콜백
스프링 빈의 라이프사이클 - 객체 생성 -> 의존 관계 주입
* but, 생성자 주입은 예외
더 정확한 스프링 빈의 이벤트 라이프 사이클
스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
-
초기화 콜백 : 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
-
소멸전 콜백 : 빈이 소멸되기 직전에 호출
스프링 빈 생명주기 콜백 지원 방법
-
인터페이스(InitializingBean, DisposableBean)
단점
-
이 인터페이스는 스프링 전용 인터페이스여서 코드가 스프링 전용 인터페이스에 의존함
-
초기화, 소멸 메서드의 이름을 변경할 수 없음
-
외부 라이브러리에 적용할 수 없음
-> 따라서 사용하지 않음
-
설정 정보에 초기화 메서드, 종료 메서드 지정
설정 정보에 @Bean(initMethod = "init", destroyMethod = "close")처럼 초기화, 소멸 메서드를 지정할 수 있음
특징
-
메서드 이름을 자유롭게 줄 수 있음
-
스프링 빈이 스프링 코드에 의존하지 않음
-
코드가 아니라 설정정보를 사용하기에 외부 라이브러리에 적용 가능
->3번 때문에 이 방식도 사용하지 않음
-
@PostConstruct, @PreDestroy 애노테이션 지원
@PostConstruct, @PreDestroy 이 두 애노테이션을 사용하면 가장 편리하게 초기화와 종료를 실행할 수 있음
특징
-
애노테이션만 붙이면 되어서 편리함
-
자바 표준이기에 스프링이 아닌 다른 컨테이너에서도 동작
-
외부라이브러리에 적용하지 못한다는 것이 유일한 단점
빈 스코프
개념
빈 스코프 : 빈이 존재할 수 있는 범위
종류
-
싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 법위의 스코프
-
프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
-
스프링 컨테이너에 요청할 때마다 새로 생성
-
종료 메서드가 호출되지 않음
-
따라서 프로토타입 빈은 프로토타입 빈을 조회환 클라이언트가 관리해야함.(종료도 클라이언트가 직접)
-
웹 관련 스코프
웹 환경에서만 동작하고 프로토타입과 다라ㅡ게 스프링이 해당 스코프의 종료시점까지 관리함.(종료메서드가 따라서 호출됨)
-
request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
-
session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
-
application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
좋은 정보 얻어갑니다.
깔끔한 정리가 압권이네요.