클라이언트 B는 clientBean을 스프링 컨테이너에 요청해서 받고, 싱글톤이므로 항상 같은 clientBean이 반환 (여기서 중요한 점, clientBean 이 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입이 끝난 빈, 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성이 된 것이지, 사용 할 때마다 새로 생성되는 것이 아님!)
클라이언트 B는 clientBean.logic()을 호출
clientBean 은 prototypeBean의 addCount()를 호출해서 프로토타입 빈의 count를 증가 ➡︎ 원래 count 값이 1이었으므로 2가 됨
프로토타입 스코프-싱글톤 빈과 함께 사용시Provider로 문제 해결
실행해보면 ac.getBean() 을 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 확인
의존관계를 외부에서 주입(DI) 받는게 아니라 이렇게 직접 필요한 의존관계를 찾는 것을 Dependency Lookup (DL) 의존관계 조회(탐색)
이렇게 스프링의 애플리케이션 컨텍스트 전체를 주입받게 되면, 스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려워짐
지금 필요한 기능은 지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 딱! DL 정도의 기능만 제공하는 무언가가 있으면 됨
ObjectFactory, ObjectProvider
지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스를 제공하는 것이 바로 ObjectProvider, 참고로 과거에 ObjectFactory 가 있었는데, 여기에 편의 기능을 추가해서 ObjectProvider 가 만들어짐
실행해보면 prototypeBeanProvider.getObject()을 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 확인
ObjectProvider의 getObject()를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환 (DL)
스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워짐
지금 딱 필요한 DL 정도의 기능만 제공
ObjectFactory: 기능이 단순, 별도의 라이브러리 필요 없음, 스프링에 의존
ObjectProvider: ObjectFactory 상속, 옵션, 스트림 처리등 편의 기능이 많고, 별도의 라이브러리 필요 없음, 스프링에 의존
JSR-330 Provider
JSR-330 자바 표준을 사용하는 방법 - 스프링 부트 3.0은 jakarta.inject.Provider 사용
실행해보면 provider.get()을 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 확인
provider의 get()을 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환 (DL)
자바 표준이고, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워짐
Provider는 지금 딱 필요한 DL 정도의 기능만 제공
get()메서드 하나로 기능이 매우 단순
별도의 라이브러리가 필요
자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능
웹 스코프
웹 스코프는 웹 환경에서만 동작
웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료시점까지 관리, 따라서 종료 메서드가 호출
웹 스코프 종류
request: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리
session: HTTP Session과 동일한 생명주기를 가지는 스코프
application: 서블릿 컨텍스트( ServletContext )와 동일한 생명주기를 가지는 스코프
애플리케이션 시작 시점에 필요한 연결을 미리 해두고,애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면,객체의 초기화와 종료 작업이 필요
스프링 빈은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료됨
초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출해야 함
스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능 제공
스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌
스프링 빈의 간단한 라이프사이클
객체 생성 ➡︎ 의존관계 주입
스프링 빈의 이벤트 라이프사이클
스프링 컨테이너 생성 ➡︎ 스프링 빈 생성 ➡︎ 의존관계 주입➡︎ 초기화 콜백➡︎ 사용➡︎ 소멸전 콜백➡︎ 스프링 종료
초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
소멸전 콜백: 빈이 소멸되기 직전에 호출
✚ 객체의 생성과 초기화를 분리
생성자: 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임
초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행
빈 생명주기 콜백의 3가지 방법
인터페이스(InitializingBean, DisposableBean)
설정 정보에 초기화 메서드, 종료 메서드 지정
@PostConstruct, @PreDestroy 애노테이션 지원
1. 인터페이스(InitializingBean, DisposableBean)
초기화: InitializingBean - afterPropertiesSet() 메서드
소멸: DisposableBean - destroy() 메서드
public class NetworkClient implements InitializingBean, DisposableBean {
...
중략
...
@Override
public void afterPropertiesSet() throws Exception {
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
disConnect();
}
}
단점
초기화, 소멸 메서드의 이름 변경 불가
외부 라이브러리에 적용 불가
초창기 방법이라 거의 사용하지 않음
2. 빈 등록 초기화, 소멸 메서드 지정
설정 정보에@Bean(initMethod = "init", destroyMethod = "close")처럼 초기화,소멸 메서드를 지정
public class NetworkClient {
...
중략
...
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
...
}
}
특징
메서드 이름 자유로움
스프링 빈이 스프링 코드에 의존하지 않음
설정 정보를 사용하기에 외부 라이브러리에도 적용 가능
3. 애노테이션 @PostConstruct, @PreDestroy
public class NetworkClient {
...
중략
...
@PostConstruct
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}