-
[Spring] 빈 생명주기 콜백Backend Dev/Spring Framework 2022. 3. 19. 14:15728x90
DB 커넥션 풀이나 네트워크 소켓처럼 앱 시작 시점에 필요한 연결을 미리 해두고, 앱 종료 시점에 연결을 모두 종료하는 작업을 진행하기 위해서 객체의 초기화와 종료 작업이 필요하다.가상의 외부 네트워크에 연결한다고 하였을 때 연결을 맺고 끊을 수 있는 클라이언트가 있다고 가정해보자.
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); } }
스프링 컨테이너를 생성해서 이 클라이언트 객체를 생성하는 빈이 등록된다고 하였을 때 생성하는 단계에서는 URL이 설정되지 않았고 생성한 후 수정자로 설정해야 생기므로 연결이 이뤄질 때 원하는 결과가 나오지 않을 것이다. 이를 어떠한 방법으로 해결하는지 살펴볼 것이다.
스프링 빈 객체를 생성하고 의존관계 주입을 마친 후 데이터를 사용할 준비가 완료되면 스프링은 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려주는 기능을 제공하고, 스프링 컨테이너가 종료되기 직전 소멸 콜백을 주어 안전한 종료 작업이 가능하다.
스프링 빈의 이벤트 라이프사이클
"스프링 컨테이너 생성" -> "스프링 빈 생성" -> "의존관계 주입" -> "초기화 콜백" -> "사용" -> "소멸전 콜백" -> "스프링 종료"
생성자는 객체를 생성하는 책임, 초기화는 생성된 값을 활용해 외부 커넥션을 연결하는 등 무거운 동작을 수행해 역할을 명확히 나눠 단일 책임 원칙을 지키고, 유지보수 관점에서 더 낫다. 이를 위해 스프링은 3가지 방법으로 빈 생명주기 콜백을 지원한다.
초기화, 소멸전 콜백 방법
- 인터페이스 InitializingBean, DisposableBean
수정자로 URL까지 주입이 완료된 후 초기화 메서드가 호출이 되고, 스프링 컨테이너 종료가 호출된 시점에는 소멸 메서드가 호출된다. 해당 코드는 스프링 전용 인터페이스에 의존하고 초기화 및 소멸 메서드의 이름을 변경할 수 없다.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 은 afterPropertiesSet() 초기화 메서드를 지원하고, DisposableBean 은 destroy() 소멸 메서드를 지원한다.
- 빈 등록 초기화, 소멸 메서드
설정 정보에 @Bean(initMethod = "init", destroyMethod = "close") 와 같이 초기화, 소멸 메서드를 지정하여, 메서드 이름을 자유롭게 줄 수 있고 스프링 빈이 스프링 코드에 의존하지 않는다.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.init"); connect(); call("초기화 연결 메시지"); } public void close() { System.out.println("NetworkClient.close"); disConnect(); } }
이 방법은 설정 정보를 사용하기에 코드를 고칠 수 없는 외부 라이브러리에서도 초기화, 종료 메서드를 적용할 수 있다. @Bean의 destroyMethod는 기본값이 (inferred)로 등록되어 있고, 종료 메서드를 추론해서 이 추론 기능은 "close", "shutdown" 이라는 통상적인 이름의 메서드를 자동으로 호출해준다. 추론 기능을 사용하지 않을 시 빈 공백(destroyMethod="")으로 지정해주어야 한다.
- 애노테이션 @PostConstruct, @PreDestroy
애노테이션 하나로 가장 편리하게 초기화와 종료를 실행할 수 있고 스프링에서 권장하는 방법이다. 스프링 종속 기술이 아닌 JSR-250 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 동작하며 컴포넌트 스캔에도 유용하게 사용된다.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.init"); connect(); call("초기화 연결 메시지"); } @PreDestroy public void close() { System.out.println("NetworkClient.close"); disConnect(); } }
단, 코드 수정이 필요해서 코드를 고칠 수 없는 외부 라이브러리에는 적용을 하지 못하므로 외부 라이브러리의 초기화 및 종료는 @Bean의 initMethod, destroyMethod를 사용해야 한다.728x90'Backend Dev > Spring Framework' 카테고리의 다른 글
[Spring] 리액트(React) - 스프링(Spring) 연동 시 발생하는 CORS 이슈 해결하기 (2) 2023.03.05 [Spring] JPA vs JDBC (0) 2022.12.20 [Spring] 동일 타입 빈 조회 (0) 2022.03.16 [Spring] 의존 관계 주입의 다양한 방식 (0) 2022.03.16 [Spring] 컴포넌트 스캔과 의존관계 자동 주입 (0) 2022.03.15