1. 객체의 초기화와 종료 작업
초기화와 소멸 콜백 기능을 통해 빈의 상태를 정확히 제어하고 이를 통해 자원을 효율적으로 관리해야한다.
1) 스프링 빈의 이벤트 생명주기
스프링 컨테이너 생성▶️스프링 빈 생성 ▶️ 의존관계 주입 ▶️ 초기화 콜백 ▶️ 사용 ▶️ 소멸전 콜백 ▶️ 스프링 종료
2) 초기화와 소멸 콜백
✔️초기화 콜백
: 객체의 생성과 의존성 주입이 완료된 후, 초기화 콜백이 호출된다.
이 단계에서는 객체가 생성된 상태와 주입된 의존성을 활용하여 필요한 초기화 작업을 수행한다.
예를 들어, 데이터베이스 연결이나 필요한 리소스의 설정이 이루어질 수 있다.
✔️소멸전 콜백
: 애플리케이션 종료 직전에 호출되는 소멸 콜백을 통해, 객체가 사용하던 리소스를 정리하고 연결을 종료하는 작업을 수행한다.
3) 객체 생성과 의존성 주입
✔️객체 생성
: 객체가 생성되는 단계에서는 필수 값들을 받아 메모리에 할당하는 작업까지만 수행된다.
✔️의존성 주입
: 생성된 객체에 필요한 의존성을 주입하는 단계.
주로 setter 주입이나 필드 주입을 통해 이루어진다.
4) 객체 생성과 초기화를 분리하자
객체의 생성과 초기화를 분리해야한다. (단일 책임 원칙을 준수해야하기 때문)
- 객체의 생성은 메모리 할당과 필수 값 설정에 집중
- 초기화 단계에서는 생성된 객체를 이용해 외부 커넥션을 연결하고 추가 설정을 수행!
2. 초기화, 종료작업 3가지 방법
- 인터페이스 InitializingBean, DisposableBean 사용
- 빈 등록 초기화, 소멸 메서드
- 애노테이션 @PostCostruct , @PreDestroy
1) 인터페이스 InitializingBean, DisposableBean 사용
✔️인터페이스가 스프링 의존적이다.
✔️ 코드 로직이 클래스에 강하게 결합되어 있어 코드의 관리와 테스트가 복잡하다.
✔️ 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.
✔️ 지금은 거의 사용하지 않는 스프링 초창기 방법이다. (2003년에 나옴)
✅인터페이스 활용 코드
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);
}
//연결이 되면 call
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();
}
}
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
//ApplicationContext가 close()를 지원하지 않아서 ConfigurableApplicationContext를 썼음
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 = null
NetworkClient.afterPropertiesSet
connect : http://hello-spring.dev
call : http://hello-spring.devmessage = 초기화 연결 메시지
12:47:05.417 [Test worker] DEBUG o.s.c.a.AnnotationConfigApplicationContext -- Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1ae67cad, started on Tue May 07 12:47:05 KST 2024
NetworkClient.destroy
close : http://hello-spring.dev
> Task :test
2) 빈 등록 초기화, 소멸 메서드
설정 정보에 @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);
}
//연결이 되면 call
public void call(String message){
System.out.println("call : " + url + "message = " + message);
}
//서비스 종료시 호출
public void disconnect(){
System.out.println("close : " + url);
}
public void init() throws Exception { //의존관계 주입이 끝나면 호출한다.
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() throws Exception { // 종료작업
System.out.println("NetworkClient.close");
disconnect();
}
}
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
//ApplicationContext가 close()를 지원하지 않아서 ConfigurableApplicationContext를 썼음
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;
}
}
}
✅테스트 결과
생성자 호출, url = null
NetworkClient.init
connect : http://hello-spring.dev
call : http://hello-spring.devmessage = 초기화 연결 메시지
17:16:20.067 [Test worker] DEBUG o.s.c.a.AnnotationConfigApplicationContext -- Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1ae67cad, started on Tue May 07 17:16:19 KST 2024
NetworkClient.close
close : http://hello-spring.dev
17:16:20.079 [Test worker] DEBUG o.s.b.f.s.DisposableBeanAdapter -- Custom destroy method 'close' on bean with name 'networkClient' completed
> Task :test
⭐특별 기능 ⭐
@Bean으로 destroyMethod를 등록했을때, inferred(추론) 기능을 쓸 수 있다.
String initMethod() default "";
String destroyMethod() default "(inferred)";
destroyMethod의 디폴트는 (inferred)이다.
외부라이브러리의 종료메서드는 대부분 'close' 혹은 'shutdown'이다.
추론 기능은 'close', 'shutdown'을 자동으로 찾아서 호출해준다.
그래서, @Bean으로 등록하면 따로 종료 메서드를 적어주지 않아도 잘 동작한다.
3) 애노테이션 @PostCostruct , @PreDestroy
✔️ 최신 스프링에서 가장 권장하는 방법.
✔️ 단, 외부라이브러리 적용하지 못한다.
✔️ 스프링 종속적인 기술이 아니라서 스프링이 아닌 다른 컨테이너에서도 동작한다.
✅ @PostCostruct , @PreDestroy 사용코드
public class NetworkClient{
//위 예제와 반복되는 코드 생략함
@PostConstruct
public void init() throws Exception { //의존관계 주입이 끝나면 호출한다.
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() throws Exception { // 종료작업
System.out.println("NetworkClient.close");
disconnect();
}
}
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest(){
//AnnotationConfigApplicationContext,ApplicationContext,ConfigurableApplicationContext 관계 알아보기
//ApplicationContext가 close()를 지원하지 않아서 ConfigurableApplicationContext를 썼음
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 = null
NetworkClient.init
connect : http://hello-spring.dev
call : http://hello-spring.devmessage = 초기화 연결 메시지
18:55:10.164 [Test worker] DEBUG o.s.c.a.AnnotationConfigApplicationContext -- Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1ae67cad, started on Tue May 07 18:55:09 KST 2024
NetworkClient.close
close : http://hello-spring.dev
> Task :test
'인프런 김영한 강의 정리 > 스프링 핵심원리 기본편' 카테고리의 다른 글
스프링 핵심원리 기본편 - 빈 스코프 (0) | 2024.05.10 |
---|---|
스프링 컨텍스트 이해와 선택: 애플리케이션 요구에 맞는 올바른 컨텍스트 사용하기 (0) | 2024.05.07 |
스프링 핵심원리 기본편 - 의존관계 자동 주입(5)|조회한 빈이 모두 필요할 때, List, Map (0) | 2024.05.07 |
스프링 핵심 원리 기본편 - 의존관계 자동 주입(4)|조회하는 빈이 2개 이상,해결법, 애노테이션 직접 만들기 (0) | 2024.05.06 |
스프링 핵심원리 기본편 - 의존관계 자동 주입(3)|롬복과 최신 트랜드 (0) | 2024.05.06 |