📌 빈 생명주기 콜백 시작
✔ 데이터베이스 커넥션 풀이나, 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면, 객체의 초기화 종료 작업이 필요!
예시
- 외부 네트워크에 미리 연결하는 객체를 하나 생성한다고 가정.(실제로 네트워크에 연결하는 것은 아니고 출력을 하도록 함)
- 이
NetworkClient
는 애플리케이션 시작 시점에connect()
를 호출해서 연결을 맺어두어야 하고, 애플리케이션이 종료되면disConnect()
를 호출해서 연결 끊어야 함
예제 코드 NetworkClient.java
🔶 test/.../hello.core/lifecycle/NetworkClient
package hello.core.lifecycle;
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = "+url);
connect();
call("초기화 연결 메시지");
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect(){
System.out.println("connect = " + url);
}
public void call(String message){
System.out.println("call: "+url + " message = "+ message);
}
//서비스 종료 시 호출
public void disconnect(){
System.out.println("close: " + url);
}
}
- constructor -> selectNone
예제 코드 BeanLifeCycleTest.java
🔶 BeanLifeCycleTest
package hello.core.lifecycle;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.close();
}
@Configuration
static class LifeCycleConfig{
@Bean
public NetworkClient networkClient(){
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
}
🔶 ac.close()
AnnotationConfigApplicationContext
로 해주거나ConfigurableApplicationContext
로 해주기
-> 실행했더니 결과가 좀 이상함
생성자 호출, url = null
connect: null
call: null message = 초기화 연결 메시지
- 생성자부분을 보면 url 정보 없이 connect가 호출됨
- 객체를 생성하는 단계에는 url이 없고, 객체를 생성한 다음에 외부에서 수정자 주입을 통해서
setUrl()
이 호출되어야 url이 존재하게 됨
라이프 사이클
- 스프링빈은 객체생성 -> 의존관계 주입의 라이프 사이클을 가짐
- 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료됨
- 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출해야 함!(완료 시점을 어떻게 아는가?)
- 스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공함
- 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌
- 안전하게 종료 완료
스프링 빈의 이벤트 라이프사이클
✔ 스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
- 초기화 콜백 : 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
- 소멸전 콜백 : 빈이 소멸되기 직전에 호출
💡 cf 1) 객체의 생성과 초기화를 분리하자
생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가짐
-> 반면, 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행함
❗ 따라서 생성자 안에 무거운 초기화 작업을 함께 하는 것보다는 객체를 생성하는 부분과 초기화하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋음!
-> 물론, 초기화 작업이 내부 값들만 약간 변경하는 정도로 단순한 경우에는 생성자에서 한 번에 다 처리하는게 더 나을 수도 있음
💡 cf 2)
싱글톤 빈들은 스프링 컨테이너가 종료될 때 싱글톤 빈들도 함께 종료되기 때문에, 스프링 컨테이너가 종료되기 직전에 소멸전 콜백이 일어남
-> 싱글톤 처럼 컨테이너의 시작과 종료까지 생존하는 빈도 있지만, 생명 주기가 짧은 빈들도 있는데 이 빈들은 컨테이너와 무관하게 해당 빈이 종료되기 직전에 소멸전 콜백이 일어남
스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원함
- 인터페이스(InitializingBean, DisposableBean)
- 설정 정보에 초기화 메서드, 종료 메서드 지정
- @PostConstruct, @PreDestroy 애노테이션 지원
인터페이스(InitializingBean, DisposableBean)
🔶 NetworkClient.java
package hello.core.lifecycle;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class NetworkClient implements InitializingBean, DisposableBean {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = "+url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect(){
System.out.println("connect = " + url);
}
public void call(String message){
System.out.println("call: " + url + " message = "+ message);
}
//서비스 종료 시 호출
public void disconnect(){
System.out.println("close: " + url);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("NetworkClient.afterPropertiesSet");
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
System.out.println("NetworkClient.destroy");
disconnect();
}
}
출력 결과
생성자 호출, url = null
NetworkClient.afterPropertiesSet
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
13:24:49.043 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing NetworkClient.destroy
close + http://hello-spring.dev
- 출력 결과를 보면 초기화 메서드가 주입 완료 후에 적절하게 호출된 것을 확인할 수 있음
- 스프링 컨테이너의 종료가 호출되자 소멸 메서드가 호출된 것도 확인할 수 있음
초기화, 소멸 인터페이스 단점
- 이 인터페이스는 스프링 전용 인터페이스임
- 해당 코드가 스프링 전용 인터페이스에 의존
- 초기화, 소멸 메서드의 이름을 변경할 수 없음
- 내가 코드를 고칠 수 없는 외부 라이브러리에 적용 불가
💡 cf) 인터페이스를 사용하는 초기화, 종료 방법은 스프링 초창기에 나온 방법들이고, 지금은 거의 사용 안함
빈 등록 초기화, 소멸 메서드
✔ 설정 정보에 @Bean(initMethod = "init", destroyMethod = "close")
처럼 초기화, 소멸 메서드를 지정할 수 있음
설정 정보를 사용하도록 변경
🔶 NetworkClient.java
package hello.core.lifecycle;
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = "+url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect(){
System.out.println("connect = " + url);
}
public void call(String message){
System.out.println("call: " + url + " message = "+ message);
}
//서비스 종료 시 호출
public void disconnect(){
System.out.println("close: " + url);
}
public void init() {
System.out.println("NetworkClient.afterPropertiesSet");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.destroy");
disconnect();
}
}
설정 정보에 초기화 소멸 메서드 지정
🔶 BeanLifeCycleTest.java
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
결과
생성자 호출, url = null
NetworkClient.init
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
13:33:10.029 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing NetworkClient.close
close + http://hello-spring.dev
✔ 설정 정보 사용 특징
- 메서드 이름을 자유롭게 줄 수 있음
- 스프링 빈이 스프링 코드에 의존하지 않음
- 코드가 아니라 설정 정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드를 적용할 수 있음
✔ 종료 메서드 추론
@Bean
의destroyMethod
속성에는 아주 특별한 기능이 있음- 라이브러리는 대부분
close
,shutdown
이라는 이름의 종료 메서드를 사용 - @Bean의
destroyMethod
는 기본값이(inferred)(추론)
으로 등록되어 있음 - 이 추론 기능은
close
,shutdown
이라는 이름의 메서드를 자동으로 호출해줌(이름 그대로 종료 메서드를 추론해서 호출해줌)- 직접 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작함
- 추론 기능을 사용하기 싫으면
destroyMethod=""
처럼 빈 공백 지정
애노테이션 @PostConstruct, @PreDestroy (권장)
🔶 NetworkClient.java
package hello.core.lifecycle;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = "+url);
}
public void setUrl(String url) {
this.url = url;
}
//서비스 시작시 호출
public void connect(){
System.out.println("connect = " + url);
}
public void call(String message){
System.out.println("call: " + url + " message = "+ message);
}
//서비스 종료 시 호출
public void disconnect(){
System.out.println("close: " + url);
}
@PostConstruct
public void init() {
System.out.println("NetworkClient.afterPropertiesSet");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.destroy");
disconnect();
}
}
@Bean(initMethod = "init", destroyMethod = "close")
헤둔건 다시@Bean
으로 바꿈
실행 결과
생성자 호출, url = null
NetworkClient.init
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
19:40:50.269 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing NetworkClient.close
close + http://hello-spring.dev
@PostConstruct
,@PreDestroy
이 두 애노테이션을 사용하면 가장 편리하게 초기화와 종료를 실행할 수 있음
@PostConstruct, @PreDestroy 애노테이션 특징
- 최신 스프링에서 가장 권장하는 방법
- 애노테이션 하나만 붙이면 되므로 매우 간단!!
- 패키지를 잘 보면
javax.annotation.PostConstruct
임- 스프링에 종속적인 기술이 아니라 JSR-250라는 자바 표준임
- 스프링이 아닌 다른 컨테이너에서도 동작
- 컴포넌트 스캔과 잘어울림
- 단점 : 외부 라이브러리에는 적용하지 못함
- 외부 라이브러리를 초기화, 종료 해야하면 @Bean 기능 사용
정리
❗@PostConstruct, @PreDestroy 애노테이션을 사용하자
- 코드를 고칠 수 없는 외부 라이브러리를 초기화, 종료해야 하면
@Bean
의inirMethod
,destroyMethod
사용
출처 : <인프런 스프링 핵심원리 - 기본편_김영한>을 수강하고 참고하여 작성한 글입니다.

PREV
[스프링 핵심 원리 - 기본편] 7. 의존관계 자동 주입
📌 다양한 의존 관계 주입 방법 🔶 의존관계 주입의 4가지 방법 생성자 주입 수정자 주입(setter 주입) 필드 주입 일반 메서드 주입 ⚡ 생성자 주입 이름 그대로 생성자를 통해서 의존관계를 주입
nyeroni.tistory.com
NEXT
[스프링 핵심 원리 - 기본편] 9. 빈 스코프
📌 빈 스코프란? 스프링 빈은 스프링 컨테이너의 시작과 함께 생성돼서 스프링 컨테이너가 종료될 때 까지 유지됨 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문 ✔ 스코프 : 빈이 존
nyeroni.tistory.com
'인프런 Spring 강의 정리 > Spring 핵심 원리 - 기본편' 카테고리의 다른 글
[스프링 핵심 원리 - 기본편] 9. 빈 스코프 (1) | 2024.01.21 |
---|---|
[스프링 핵심 원리 - 기본편] 7. 의존관계 자동 주입 (3) | 2024.01.21 |
[스프링 핵심 원리 - 기본편] 6. 컴포넌트 스캔 (1) | 2024.01.20 |
[스프링 핵심 원리 - 기본편] 5. 싱글톤 컨테이너 (0) | 2024.01.20 |
[스프링 핵심 원리 - 기본편] 4. 스프링 컨테이너와 스프링 빈 (1) | 2024.01.20 |