일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch14
- 스프링 mvc2 - 검증
- 자바 중급1편 - 날짜와 시간
- 데이터 접근 기술
- 자바 고급2편 - io
- 스프링 mvc2 - 타임리프
- 자바 기초
- 자바의 정석 기초편 ch2
- 스프링 mvc1 - 스프링 mvc
- 스프링 트랜잭션
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch5
- 자바로 키오스크 만들기
- 스프링 입문(무료)
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch6
- 람다
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch7
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch11
- 자바의 정석 기초편 ch4
- 자바로 계산기 만들기
- 자바의 정석 기초편 ch9
- 자바 고급2편 - 네트워크 프로그램
- @Aspect
- 자바 중급2편 - 컬렉션 프레임워크
- 스프링 고급 - 스프링 aop
- Today
- Total
개발공부기록
Bean Scope와 종류(아는 만큼만) 본문
Bean Scope
정의
말 그대로 @Autowired나 @Configuration과 @Bean으로 등록한 스프링 빈이 존재할 수 있는 범위(생명 주기)를 뜻한다
존재할 수 있는 범위라고하면 '등록된 빈이 등록되고 사라지는 시점의 범위가 어디인가'라고 보면 될 것 같다.
스프링 빈은 객체를 생성하고 의존관계 주입이 다 끝난 다음에 필요한 데이터를 사용할 수 있는 준비가 완료 되기 때문에 초기화 작업을 진행 해야한다.
스프링은 의존관계 주입이 완료 되면 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려주는 다양한 기능을 제공하며 스프링 컨테이너가 종료되기 직전에 소멸 콜백(싱글톤)을 주어 안전하게 종료 작업을 진행할 수 있다
스프링 빈의 기본 생명 주기
스프링 컨테이너 생성 -> 스프링 빈 생성(생성자 주입) -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
초기화 콜백 : 빈이 생성되고 빈의 의존관계 주입이 완료 된 후 호출
소멸전 콜백 : 빈이 소멸되기 직전에 호출
스프링이 지원하는 빈 생명주기 콜백 3가지 방법
인터페이스(InitializingBean, DisposableBean)
- 스프링 초창기에 나온 방법이며 거의 사용하지 않는다
설정 정보에 초기화 메서드, 종료 메서드 지정
public class NetworkClient {
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
네크워크 클라이언트가 있다고 있을 때 애플리케이션이 시작할 때 init() 메서드가 호출되어 네트워크 연결이 되고 애플리케이션이 종료될 때 close()메서드가 호출되어 연결이 종료되어야 한다고 가정한다.
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
설정 클래스를 작성하여 스프링빈에 등록할 때 @Bean(initMethod = "초기화메서드이름", destryoMethod = "종료메서드이름") 처럼 초기화 메서드와 종료 메서드를 지정해주면 생명주기 콜백에 의존관계가 주입되고난 후 init() 메서드가 호출되고, 애플리케이션이 종료되기 직전에 clolse()메서드고 호출되는 모습을 확인할 수 있다
생성자 호출, url= null
NetworkClient.init
connect: http://hello-spring.dev
call: http://hello-spring.dev message= 초기화 연결 메시지
NetworkClient.close
close: http://hello-spring.dev
> Task :test
BUILD SUCCESSFUL in 838ms
4 actionable tasks: 1 executed, 3 up-to-date
오후 3:27:12: Execution finished ':test --tests "hello.core.lifecycle.BeanLifeCycleTest"'.
이렇게 설정정보에 옵션을 통해 빈 생명주기 콜백을 활용하면 스프링 빈이 스프링 코드에 의존하지 않아 메서드 이름을 자유롭게 작성하여 원하는 동작을 설정할 수 있고, 코드를 고칠 수 없는 외부라이브러리를 사용할 때에도 초기화 메서드와 종료 메서드를 각각 애플리케이션 시작시점과 종료 시점에 원하는 동작을 지정할 수 있다.
** 참고 - 종료 메서드 추론
- @Bean의 destroyMethod 속성은 close(), shutdown()이라는 메서드를 자동으로 호출하는 특별한 기능이 있다.
- 라이브러리들 대부분은 close, shutdown이라는 이름의 종료 메서드를 사용하는데, @Bean의 destroyMethod는 기본값이 inferred(추론)으로 등록되어있어 종료메서드를 추론하여 종료 메서드를 추론하여 자동으로 호출한다.
- 즉, 직접 @Bean을 사용하여 수동으로 스프링 빈을 등록하면 종료 메서드를 따로 적어주지 않아도 잘 동작하며 추론 기능을 사용하기 싫다면 destoryMethod 옵션에 "" 빈 문자열을 입력해주면 된다.
애노테이션 @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();
}
}
@Configuration
static class LifeCycleConfig {
@Bean
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
생명주기 콜백을 이용할 때 스프링에서 가장 권장되는 방식으로 스프링에 종속적이지만 애노테이션 하나만 붙이면 되기 때문에 매우 편리하게 콜백 메서드를 작성할 수 있다
해당 애노테이션들도 스프링에 종속적인 것이 아니라 자바 표준 기술이기 때문에 다른 컨테이너에서도 동작하지만 외부 라이브러리에 적용할 수 없다는 단점이 있다.
기본적인 상황에서는 @PostConstruct, @PreDestroy 애노테이션을 사용하면 되며 코드를 고칠 수 없는 외부 라이브러리에 생명주기 콜백을 활용할 때에는 @Bean의 initMethod, destroyMethod를 활용하면 된다.
Scope의 종류
스코프 지정 방법
@Scope 애노테이션을 통해 스코프를 직접 지정할 수 있다.
// 컴포넌트 스캔 자동 등록
@Scope("prototype")
@Component
public class HelloBean {}
// 수동 등록
@Scope("singleton")
@Bean
PrototypeBean HelloBean() {
return new HelloBean();
}
싱글톤 스코프
스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위위 스코프로 스프링 빈의 기본 스코프이다.
스프링 빈은 기본적으로 싱글톤 스코프로 생성되기 때문에 특별한 설정이 없다면 스프링 빈은 스프링 컨테이너가 시작하고 종료될 때까지 유지된다.
프로토타입 스코프
스프링 컨테이너가 프로토타입 빈의 생성과 의존관계까지만 관여하고, 그이후에는 관리하지 않는 매우 짧은 범위위 스코프이다.
싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환하지만 프로토 타입 빈은 항상 새로운 인스턴스를 생성해서 반환한다.
즉, 빈 스코프가 프로토 타입이면 스프링 컨테이너가 프로토 타입 빈을 생성하고 의존관계 주입 초기화 까지만 처리하며 클라이언트에 빈을 반환한 이후에는 스프링 컨테이너가 생성된 프로토 타입 빈을 관리하지 않기 때문에 @PreDestory 같은 종료 메서드가 호출되지 않는다.
기본적으로 싱글톤 빈으로 대부분의 문제를 해결하기 때문에 프로토타입 빈을 설정하여 사용하는 경우는 없지만 매번 스프링 빈을 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요할 때 사용하면 된다.
프로토 타입 스코프 주의점
싱글톤 빈과 함께 사용한 프로토 타입의 스코프 빈은 항상 새로운 객체 인스턴스를 생성 및 반환하지 않을 수 있다
public class SingletonWithPrototypeTest1 {
@Test
void prototypeFind2() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(2);
}
@Scope // 싱글톤 빈
static class ClientBean {
private final PrototypeBean prototypeBean; // 생성 시점에 주입
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init "+ this);
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
해당 코드를 보면 싱글톤 빈인 ClientBean이 생성자를 통해 PrototypeBean을 주입받고 클라이언트는 싱글톤 빈인 ClientBean을 사용하여 메서드를 호출하고 있다.
싱글톤 빈인 ClientBean 내부에서 프로토 타입 빈을 주입받아 사용하기 때문에 getBean(ClientBean.class)로 호출할 때마다 각각 다른 인스턴스가 생성될 것 같지만 실제로 테스트를 해보면 하나의 싱글톤 빈에서 관리되어 값을 증가시키는 logic() 메서드를 clientBean1, clientBean2로 한번씩 호출해보면 2가 되어 테스트가 통과되는 것을 확인할 수 있다.

즉, 싱글톤이 clieanBean이 내부에 가지고 있는 프로토 타입 빈은 과거에 이미 주입이 끝난 빈을 가지고 의존관계 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 생성된 것이지 사용할 때마다 인스턴스가 새로 생성되는 것이 아니다.
싱글톤 빈은 생성시점에만 의존관계 주입을 받기 때문에 프로토타입 빈이 새로 생성되기는 하지만 싱글톤 빈과 함께 계속 유지되기 때문에 함께 사용하게되면 이런 문제가 발생하므로 프토로타입 빈을 사용할 때는 ObjectProvider나 JSR-330 Provider를 사용하면 된다.
ObjectProvider 사용 방법
지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스를 제공하는 ObjectFactory(과거 버전)와 ObjectFactory를 상속하고 다양한 옵션과 스트림처리 등의 편의 기능이 추가된 ObjectProvider(새로운 버전)가 있다.
// ObjectFactory, ObjectProvider
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
// ClientBean의 logic 메서드가 호출될 때마다 프로토타입의 빈이 조회되어 계속 생성됨
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
JSR-330 Provider 사용 방법
라이브러리를 등록하고 Provider를 선언하여 provider.get()을 하면 프로토타입 빈이 계속 생성된다.
implementation 'jakarta.inject:jakarta.inject-api:2.0.1'
static class ClientBean {
@Autowired
private Provider<PrototypeBean> provider;
// ClientBean의 logic 메서드가 호출될 때마다 프로토타입의 빈이 조회되어 계속 생성됨
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
웹 관련 스코프
request
- 웹 요청이 들어오고 나갈 때 까지 유지 된다
- 즉, 각각의 클라이언트가 HTTP 요청을 시작하면 생성이 되고 요청 처리가 끝나면 스프링 빈이 제거되는 범위이다.
- 동시에 여러 HTTP 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어려운데 이럴때 request 스코프를 사용하면 좋다고 한다.
session
- 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프로 로그인한 사용자 정보를 저장할 때 사용하고 브라우저를 닫거나 세션 타임아웃이 되면 종료된다.
application
- 웹의 서블릿 컨텍스트와 같은 범위로 유지되므로 싱글톤 빈보다 범위가 더 넓기 때문에 전체 사용자와 공유해야 하는 애플리케이션 공통 설정, 통계 정보, 전역 캐시 등의 데이터를 저장할 때 사용된다.
webSocket
- 웹 소켓과 동일한 생명주기를 가지는데, 스프링에서 공식 지원하는 스코프가 아니므로 @Scope("websocket")과 같이 작성하는 것이 아니라 별도로 직접 구현해야 한다.
- 채팅 애플리케이션에서 사용자 한 명의 WebSocket이 연결되고 종료되는 범위이이다.
'이론 직접 정리 > 스프링' 카테고리의 다른 글
IoC, DI는 무엇이고 어떠한 장점이 있을까? (0) | 2025.03.27 |
---|