[TIL-Spring] 6. 빈 생명주기 콜백

jgoneit·2023년 4월 26일
0

Spring

목록 보기
7/8
post-thumbnail

1. 빈 생명주기 콜백

스프링 컨테이너는 Bean객체들을 관리한다. 스프링 컨테이너를 IoC컨테이너라고도 일컫는데, 스프링 컨테이너가 IoC의 설계원칙을 따르기 때문이다.

IoC(Inversion of Control)는 이전에 언급했듯이 제어의 역전을 말한다. 즉 Bean객체들을 관리하는 주체가 개발자가 아닌 스프링 컨테이너가 대신 수행한다는 것이다.

DB 커넥션 풀이나 네트워크 소켓 연결 등 어플리케이션 시작 시점에 필요한 연결들을 미리한 뒤, 종료시점에 연결된 것들을 모두 종료하는 작업이 필요하다.

여기서 먼저 필요한 연결을 하려면 필요한 데이터를 사용할 수 있는 준비가 되어야한다. 필요한 데이터를 사용하기 위해서는 빈 객체를 생성하고 의존관계 주입을 끝낸 후에 사용가능하다. 어플리케이션 시작 시점과 종료시점을 어떻게 알 수 있을까?

스프링에서는 다행히 의존관계 주입이 완료된 후, 스프링 컨테이너가 종료되기 직전에 콜백을 통해서 알려주는 다양한 기능을 제공해준다. 스프링에서 제공하는 기능을 통해서 손쉽게 콜백을 받을 수 있다.

💡 콜백(Callback)
콜백함수를 부를때 사용되는 용어로 특정이벤트가 발생했을 때 해당 메소드가 호출(비동기)된다.

2. 스프링 빈의 이벤트 라이프사이클

  1. 스프링 컨테이너 생성
  2. 스프링 빈 생성
  3. 의존관계 주입
  4. 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
  5. 사용
  6. 소멸 전 콜백: 빈이 소멸되기 직전에 호출
  7. 스프링 컨테이너 소멸

라이프 사이클을 보면 스프링 빈 생성, 의존관계 주입, 초기화 콜백이 나누어진 것을 볼 수 있다. 생성자 주입을하면서 빈을 생성하고 생성자 주입 내에서 초기화 콜백을 수행하면 될 것같은데 분리 되어있다.

💡 객체의 생성과 초기화의 분리
초기화 작업에서 값들의 변경이 적거나 코드가 전체적으로 단순할 때는 라이프 사이클을 압축해서 생성자 주입때 한번에 처리하는 것이 더 효율적일 수 있다.

그러나 코드가 단순한 경우가 아니라면 초기화 작업에는 생성자에서 생성된 객체의 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행하는데, 생성자안에서 이런 복잡한 과정들을 한꺼번에 작성하는 것보다 생성과 초기화 부분을 분리하면 유지보수 관점에서 훨씬 효율적이다.

3. 빈 생명주기 콜백의 3가지 방식

  • 인터페이스(InitializingBean, DisposableBean)
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • @PostConstruct, @PreDestroy 애너테이션 지정

1. 인터페이스(InitializingBean, DisposableBean)

인터페이스 InitializingBean, DisposableBean을 상속바당 구현체를 작성하는 방식으로, InitializingBean은 메소드를 초기화하는 초기화 콜백을 지원한다. 그리고 DisposableBean메소드 소멸 전 콜백을 지원한다.

사용법은 다음과 같다
| BeanCallbackEx

public class BeanCallbackEx implements InitializingBean, DisposableBean {
	
    @Override
    public void afterPropertiesSet() throws Exception {
		connect();
        call("초기화 콜백");
    }
    
    @Override
    public void destroy() throws Exception {
    	disconnect();
    }
}

단점

  • 인터페이스 InitializingBean과 DisposableBean은 스프링 전용 인터페이스로, 스프링 전용 인터페이스에 의존.
  • 초기화, 소멸 메소드의 메서드명을 변경 ❌(오버라이드 하기 때문)
  • 커스터마이징이 불가능한 외부라이브러리에 적용 ❌

인터페이스를 사용하는 방법은 스프링 초창기 출시된 방법으로 지금은 거의 사용되지 않는다.

2. 설정 정보에서의 지정

설정 정보에서 메서드를 지정하면 인터페이스 방식의 단점이었던 메서드 명의 변경불가, 스프링 인터페이스에 의존, 외부라이브러리의 적용불가를 모두 해결할 수 있다. 먼저 코드로 알아보자

| BeanCallbackConfig

public class BeanCallbackConfig {
	@Bean(initMethod = "init", destroyMethod = "close")	// 빈 등록 시점에 초기화 메서드, 종료메서드 지정
    public BeanCallback beanCallback() {
    	...
    }
}

| BeanCallbackEx

public class BeanCallbackEx {

	public void init() {
    	connect();
        call("초기화 콜백");
    }
    
    public void close() {
    	disconnect();
    }
}

설정정보에서 지정해주는 방식은 스프링 빈을 등록할 때 초기화 메소드와 종료 메소드를 지정할 수 있다. 설정 정보 중 destroyMethod속성에는 아주 특별한 기능이 있다. 이 특별한 기능은 추론 기능인데 destryMethod의 기본값이 inferred(추론)으로 등록되어 있다.

라이브러리에서 종료 메서드의 메서드명은 대부분 close또는 shutdown을 사용하는데 이 추론 기능이 closeshutdown의 이름을 가지고 있는 메서드를 자동으로 호출해주는 기능이다. 그렇기 때문에 종료 메서드를 따로 설정하지 않아도 종료메서드를 호출해서 동작시킨다.
만약 이 추론 기능을 사용하지 않으려면 destryMethod=""으로 지정하면 된다.

3. @PostConstruct & @PreDestroy

@PostConstruct@PreDestroy 애너테이션을 사용하는 방식이 최신 스프링에서 가장 권장하는 방법이다. 구성정보 클래스에서 따로 작성해주지않고 메서드에 애너테이션 하나만 작성하면 되므로 매우 편리하다.

또한 javax.annotation.*패키지에 속해 있는 기능으로 스프링 종속 기능이 아니라 JSR-250이라는 자바표준 기술로 스프링 환경이 아니어도 동작하며 컴포넌트 스캔과 함께 사용할 수 있다.

하지만 인터페이스를 사용할 때의 단점이었던 커스터마이징이 불가능한 외부라이브러리에서 적용이 불가하다는 단점이 있어 외부 라이브러리에서 작동시켜야 할 경우는 위의 설정정보에서 지정하는 방식을 채택해서 사용해야 한다.

사용 코드는 다음과 같다.

| BeanCallbackEx

public class BeanCallbackEx {

	@PostConstruct
    public void init() throws Exception {
    	connect();
        call("초기화 콜백");
    }
    
    @PreDestroy
    public void close() throws Exception {
    	disconnect();
    }
}

📚 Reference

profile
be to BE Dev

0개의 댓글

관련 채용 정보