스프링 빈(싱글톤)의 생명 주기
스프링 컨테이너 생성 -> 스프링 빈 생성(생성자 함수 실행, 생성자 주입 실행) -> 의존관계 주입(생성자 주입을 제외한 의존관계 주입) -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
초기화 콜백, 소멸전 콜백은 스프링에서 자체적으로 지원하는 기능이다.
코드로 예시를 살펴보자.
#AutoConnect
public class AutoConnect {
private String fieldA;
public AutoConnect() {
System.out.println("AutoConnect 객체 생성됨");
System.out.println("field=" + fieldA);
connect();
call("초기화 연결 메시지");
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
// 서비스 시작시 호출
public void connect(){
System.out.println("connect:" + fieldA);
}
public void call(String message){
System.out.println("CallField:" + fieldA +", message :" + message);
}
// 서비스 종료시 호출
public void disconnect(){
System.out.println("close:" + fieldA);
}
}
#테스트 코드
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
AutoConnect client = ac.getBean(AutoConnect.class);
ac.close();
}
@Configuration
static class LifeCycleConfig{
@Bean
public AutoConnect autoConnect(){
System.out.println("빈 등록함수 호출");
AutoConnect autoConnect = new AutoConnect();
System.out.println("객체 생성");
autoConnect.setFieldA("aaaaaaaaaa");
System.out.println("필드값 셋팅 완료");
return autoConnect;
}
}
}
위의 코드처럼 객체를 먼저 생성하고 외부에서 필드값을 받거나 의존주입을 받는 경우, 생성자 함수가 사용자의 의도대로 동작하지 않는다.
스프링의 생명주기 콜백
스프링에서 지원하는 생명주기 콜백은 크게 3가지다.
1. 인터페이스 InitializingBean, DisposableBean
2. 설정 정보에 초기화 메서드, 종료 메서드 지정
3. @PostConstruct, @PreDestroy 어노테이션 지원
1. 인터페이스
# AutoConnect
// 인터페이스 상속
public class AutoConnect implements InitializingBean, DisposableBean {
private String fieldA;
public AutoConnect() {
System.out.println("AutoConnect 객체 생성됨");
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
// 서비스 시작시 호출
public void connect(){
System.out.println("connect:" + fieldA);
}
public void call(String message){
System.out.println("CallField:" + fieldA +", message :" + message);
}
// 서비스 종료시 호출
public void disconnect(){
System.out.println("close:" + fieldA);
}
// 오버라이딩한 메소드에 코드 입력
@Override
public void destroy() throws Exception {
System.out.println("field=" + fieldA);
connect();
call("초기화 연결 메시지");
}
@Override
public void afterPropertiesSet() throws Exception {
disconnect();
}
}
이 방법을 사용하면 자동적으로 스프링이 객체 생성 + 초기화까지 완료된 다음 자동으로 afterPropertiesSet 내부 코드를 실행하는걸 볼 수 있다.
이 방법은 스프링 초창기에 나온 방법으로, 스프링 프레임워크에 의존한다는 단점과 메서드 이름 변경 불가, 외부 라이브러리 사용 불가의 단점이 존재해 지금은 쓰이지 않는 방법이다.
2. 설정 정보를 이용한 메서드 지정
@Configuration 클래스를 사용해 빈을 등록할 시, @Bean 의 옵션을 지정해 초기화, 소멸 메서드를 지정해줄수 있다.
@Bean(initMethod = "[초기화 메서드 이름]", destroyMethod = "[소멸 메서드 이름]")
이를 활용하면 지정된 메서드가 자동으로 실행된다.
이때 해당 메서드는 설정 파일이 아닌, 빈으로 등록될 클래스 내부에 있어야 한다.
# AutoConnect
public class AutoConnect {
private String fieldA;
public AutoConnect() {
System.out.println("AutoConnect 객체 생성됨");
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
// 서비스 시작시 호출
public void connect(){
System.out.println("connect:" + fieldA);
}
public void call(String message){
System.out.println("CallField:" + fieldA +", message :" + message);
}
// 서비스 종료시 호출
public void disconnect(){
System.out.println("close:" + fieldA);
}
// Config에서 설정한 이름과 동일한 메서드
public void init(){
connect();
}
public void close(){
disconnect();
}
}
# BeanLifeCycleTest
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
AutoConnect client = ac.getBean(AutoConnect.class);
ac.close();
}
@Configuration
static class LifeCycleConfig{
//빈 옵션 등록
@Bean(initMethod = "init", destroyMethod = "close")
public AutoConnect autoConnect(){
System.out.println("빈 등록함수 호출");
AutoConnect autoConnect = new AutoConnect();
System.out.println("객체 생성");
autoConnect.setFieldA("aaaaaaaaaa");
System.out.println("필드값 셋팅 완료");
return autoConnect;
}
}
}
결과:
이 방식은 메서드 이름을 자유롭게 설정할수 있으며 빈 클래스가 스프링에 의존하지 않는다. 또한 외부 라이브러리 역시 적용할수 있다.
*destroyMethod 의 경우, 따로 설정해주지 않으면 close나 shutdown이라는 이름의 메서드를 자동으로 호출해준다.
3. 어노테이션
config 클래스를 사용하는 방식이 간소화된 것이라고 생각하면 편하다.
# AutoConnect
public class AutoConnect {
private String fieldA;
public AutoConnect() {
System.out.println("AutoConnect 객체 생성됨");
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
// 서비스 시작시 호출
public void connect(){
System.out.println("connect:" + fieldA);
}
public void call(String message){
System.out.println("CallField:" + fieldA +", message :" + message);
}
// 서비스 종료시 호출
public void disconnect(){
System.out.println("close:" + fieldA);
}
// 어노테이션 사용
@PostConstruct
public void init(){
connect();
}
@PreDestroy
public void close(){
disconnect();
}
}
최신 스프링에서 가장 권장하는 방법으로 편리하다. 또한 스프링에 종속적인 기술이 아닌 자바 표준이기에 다른 컨테이너에서도 작동한다는 장점을 가지고 있다.
다만 외부 라이브러리 적용은 불가능하기 때문에 외부 라이브러리 사용시에는 @Bean을 사용해야 한다.