빈 생명주기 콜백은 스프링 빈이 생성되거나 종료될 때 빈 안의 메소드를 호출하는 것이다.
목차
- 빈 생명주기 콜백 시작
- 인터페이스 InitalizingBean, DisposableBean
- 빈 등록 초기화, 소멸 메서드
- 애노테이션 @PostConstruct, @PreDestory
빈 생명주기 콜백 시작
DBCP(데이터베이스 커넥션 풀), 네트워크 소켓 등 애플리케이션 시작 전 필요한 연결을 미리 하거나 종료될 때 안전하게 종료할 수 있는 작업을 진행하려면 객체의 초기화와 종료 작업이 필요하다.
예제로 어떻게 초기화와 종료 작업이 이루어지는지 알아보자.
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);
}
}
외부 네트워크에 미리 연결하는 객체를 하나 생성하자. (예제에서는 실제로 네트워크에 연결하지 않고 문자만 출력시킴)
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext 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;
}
}
}
위를 실행시킬 때 원하는 동작은 생성자가 호출되며 url이 연결되어 "생성자 호출, url = " + http://hello-spring.dev가 출력이다.
생성자 호출, url = null
connect: null
call: null message = 연결 메시지
실제로 출력된 내용은 아래와 같다.
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
예시 코드를 다시 보면 생성자를 호출하는 순간 이후에 설정이 들어오기 때문에 null로 출력된다.
객체 생성 → 의존관계 주입
스프링 빈은 위 와 같은 라이프사이클을 가짐
스프링 빈은 객체 생성과 의존관계 주입이 끝난 뒤에 데이터를 사용할 수 있는 준비가 된다.
그렇기 때문에 의존관계 주입이 끝남과 동시에 데이터를 사용하고 싶다면 콜백 메서드를 사용하여야 한다. 또한 스프링 컨테이너가 종료되기 직전에 소멸콜백을 보낸다.
스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸전 콜백→ 스프링 종료
스프링 빈의 이벤트 라이프 사이클
- 초기화 콜백 : 빈이 생성된 후, 의존관계 주입이 완료된 시점에 호출
- 소멸전 콜백 : 빈이 소멸되기 직전에 호출
참고로 생성자를 통해 초기화, 설정 정보 등을 주입하지 않는 이유는 코드의 기능을 명확하게 역할을 나누기 위해서이다. (유지보수 관점에서 좋음) 초기화 작업이 사소한 값 변경 등과 같은 경우에는 생성자에서 한번에 처리하는게 더 나을 수도 있어서 상황에 맞추어서 사용하자.
스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다.
- 인터페이스(InitalizingBean, DisposableBean)
- 설정 정보에 초기화 메서드, 종료 메서드 지정
- @PostConstruct, @PreDestory 애노테이션 지원
1. 인터페이스 콜백
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 {
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
disconnect();
}
}
기존 소스에서 InitializingBean, DisposableBean 인터페이스를 상속받았다.
상속과 함께 인터페이스 메소드를 구현하게 되는데 이 메소드가 빈 초기화와 종료 시 호출되는 메소드이다.
출력값
생성자 호출, url = null
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
17:18:01.138 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@ba8d91c, started on Thu Jul 15 17:18:00 KST 2021
close: http://hello-spring.dev
생성자 호출 다음 url이 대입이 되고, 생성 끝난 뒤 빈 종료에 destory 메소드가 생성된다.
초기화, 소멸 인터페이스의 단점
- 스프링 전용 인터페이스이므로 스프링에 의존한다.
- 초기화, 소멸 메서드의 이름을 변경할 수 없다.
- 내가 코드를 고칠 수 없는 외부 라이브러리에는 사용할 수 없다.
스프링 초창기에 나온 방법이며 많이 사용하지 않는다.
2. 빈 등록 초기화, 소멸 메서드
빈 애노테이션에 직접 초기화, 소멸 메서드를 지정하는 것이다.
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() {
connect();
call("초기화 연결 메시지");
}
public void close() {
disconnect();
}
}
인터페이스를 없애고 메소드를 init과 close로 바꾸었다.
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.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와 destroyMethod에 메소드 이름을 지정해주면 완료
생성자 호출, url = null
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
17:38:34.479 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@ba8d91c, started on Thu Jul 15 17:38:34 KST 2021
close: http://hello-spring.dev
앞서 보았던 똑같은 출력값을 받을 수 있다.
설정 정보 사용 특징
- 메서드 이름을 자유롭게 줄 수 있다.
- 스프링 빈이 스프링 코드에 의존하지 않는다.
- 코드가 아니라 설정정보에 들어가기 때문에 외부 라이브러리에도 세팅할 수 있다.
(@Bean의 destoryMethod은 default 인자값으로 close를 사용하고 있어서 따로 종료 메서드를 적지 않아도 close 혹은 shutdown 이름의 메서드를 호출한다. 추론 기능을 사용하기 싫다면 destoryMethod=""를 사용하여야 함)
3. 애노테이션을 이용한 콜백(PostConstruct, PreDestory)
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() {
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
disconnect();
}
}
대상 메서드에 @PostConstruct, @PreDestory 를 달아주면 완료
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
두 애노테이션의 import package 경로를 보면 위와 같다.
javax는 자바에서 공식적으로 지원하는 api이므로 스프링이 아니여도 사용할 수 있다.
특징
- 최신 스프링에서 권장하는 방법
- 애노테이션 하나만 붙여도 되므로 매우 편리
- 스프링에 종속적인 기술이 아닌 JSR-250이라는 자바 표준이다.
- 컴포넌트 스캔과 잘어울린다.
- 유일한 단점은 외부 라이브러리에 적용하지 못한다는 것이다.
정리
- @PostConstruct, @PreDestory 애노테이션을 사용하면 된다.
- 외부라이브러리에 사용하려면 빈 등록, 초기화 메소드를 사용하자.
'JAVA > Spring' 카테고리의 다른 글
[Spring] 웹 스코프 (0) | 2021.07.16 |
---|---|
[Spring] 빈 스코프(singleton bean, prototype bean) (0) | 2021.07.16 |
[Spring]컴포넌트 스캔과 의존관계 자동 주입 (Component Scan, Autowired) (0) | 2021.07.14 |
[Spring] 스프링과 싱글톤 (0) | 2021.07.13 |
[Spring] 스프링 빈 조회하기 (0) | 2021.07.09 |