Bean 생명주기란?
스프링 컨테이너는 Bean을 주입하는 것 이외에 생성과 소멸과 같은 생명 주기를 관리한다.
빈 생명주기
스프링 빈은 객체 생성 후, 의존관계 주입이 다 끝난 후에 필요한 데이터를 사용할 수 있는 준비가 완료된다.
-> 여기서 사용할 수 있는 준비라는 것은 빈이 생성되는 것을 말하는 건가?
따라서 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에야 호출한다.
의존관계 주입이 완료되면, 스프링 빈에게 콜백 메소드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다. 또한 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다.
스프링 빈의 이벤트 라이프사이클
스프링 컨테이너 생성-> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료
초기화 콜백: 빈이 생성되고, 빈의 의존 관계 주입이 완료된 후 호출
소멸전 콜백: 빈이 소멸되기 직전에 호출
+ 객체의 생성과 초기화를 분리하는 이유: 생성자는 파라미터를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가지는 반면에 초기화는 생성된 값들을 사용하여 외부 동작은 수행하기 때문에 명확하게 나누는 것이 유지보수 관점에서 좋다.
@PostConstruct - InitializingBean, @PreDestroy - DisposableBean
해당 어노테이션은 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 동작한다. 유일한 단점은 외부 라이브러리에는 적용하지 못한다는 점이다.
스프링 2.5 이전에는 스프링에서 제공하는 InitializingBean, DisposableBean 인터페이스를 통해, afterPropertiesSet(), destroy() 콜백 메서드를 정의하여 사용한다.
만약 외부 라이브러리에 초기화, 소멸 메소드를 설정해줘야 한다면, initMethod, destroyMethod를 사용한다.
빈 스코프란
빈이 존재할 수 있는 범위(Scope)이다.
or
빈의 생성 방식이라고 생각해도 되겠다.
Bean 스코프 종류
싱글톤
프토타입
웹 관련 스코프
- request: 웹 요청이 들어오고 나갈 때까지 유지되는 스코프이다.
- session: 웹 세션이 생성되고 종료될 때까지 유지되는 스코프이다.
- application: 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프이다.
- websocket: websocket라이프 사이클 안에서 한 개의 빈만 사용한다.
프로토타입 스코프
Bean을 생성하여 주입할 때, 항상 새로운 인스턴스를 생성하여 주입한다.
싱글톤 Bean은 스프링 컨테이너가 관리하며 컨테이너가 종료될 때 Bean의 종료 메소드가 실행되지만, 프로토타입 Bean은 스프링 컨테이너가 생성과 의존 관계 주입 그리고 초기화까지만 관여하고 더는 관리하지 않는다.
즉, 앞에서 언급한 @PreDestroy같은 메소드가 실행되지 않는다.
또한 프로토타입 Bean은 조회한 클라이언트가 관리해야 한다. 종료 메소드에 대한 호출도 클라이언트가 직접해야 한다.
프로토타입 스코프 사용법
프로토타입 빈과 싱글톤 빈을 함께 사용할 떄 문제점
1. 싱글톤안에서 프로토타입을 사용하게 되는 경우
프로토타입을 사용하게 되는 이유는 항상 새로운 인스턴스를 생성하기 위함일 것이다.
하지만 A라는 싱글톤 Bean안에 B라는 프로토타입 Bean을 주입받은 인스턴스라면 어떤 상황이 발생할까?
B는 계속해서 새로운 인스턴스를 생성하길 의도했겠지만, A가 싱글톤이기 때문에 B 인스턴스를 새로 생성해서 주입하지 않는다.
그렇다면 어떻게 해결할 수 있을까?
solution
1. 프록시 빈으로 문제를 해결
가장 간단한 방법으로는 싱글톤 Bean이 프로토타입을 사용할 때 마다 스프링 컨테이너에 새로 요청하는 방법이 있다.
의존 관계를 외부에서 주입 받는게 아니라 이렇게 직접 필요한 의존관계를 찾는 것을 DL(Dependency Lookup) 의존관계 조회라고 한다.
하지만 이렇게 스프링의 애플리케이션 컨텍스트 전체를 주입받게 되면, 스프링 컨테이너에 종속적인 코드가 되고 단위 테스트도 어려워진다.
만약 해당 오브젝트가 클래스가 아니라 인터페이스라면 proxyMode = ScopedProxyMode.INTERFACE로 사용하면 된다.
이 원리가 무엇인가?
기본적으로 싱글톤 안에서 프로토타입을 직접 사용하면, 바꿔줄 여지가 없어진다.
위 예시에서의 해결책은 클래스 기반의 프록시로 감싸는 것이다.
그리고 빈을 등록할 때, 해당 프록시 빈을 등록하여 사용하는 것이다.
ScopedProxyMode는 어떤 기반의 프록시 빈을 만들지 설정해 주는 것이다.
위 예시는 프록시기반의 클래스를 만든 것이고 만약 interface로하면, 인터페이스 기반의 프록시를 만들어서 썼을 것이다.
2. ObjectProvider 객체를 사용하여 해결
@Component
public class Single {
@Autowired
ObjectProvider<ProtoType> protoType;
public ProtoType getProtoType() {
return protoType.getIfAvailable();
}
}
ObjectProvider<>를 사용해서, 사용될 때 마다 Bean을 주입하는 방식으로 해결할 수 있다.
하지만 1번이 POJO를 유지하기 때문에 더 선호된다고 한다.
참고: https://velog.io/@hyungjungoo95/Spring-%EB%B9%88-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%BD%9C%EB%B0%B1, https://velog.io/@probsno/Bean-%EC%8A%A4%EC%BD%94%ED%94%84%EB%9E%80
'개발 > Spring' 카테고리의 다른 글
[프로그래머스] Level2_오픈채팅방 (0) | 2021.07.08 |
---|---|
[Spring] Spring MVC 전체 구조 (0) | 2021.06.14 |
[Spring] 컴포넌트 스캔, 의존 관계 자동 주입 (0) | 2021.05.07 |
[Spring] IoC 컨테이너 Environment 프로파일, 프로퍼티 (0) | 2021.05.03 |
[Spring] 빈의 스코프 (0) | 2021.05.03 |