본문 바로가기

Java

[Spring] Bean 생명주기

반응형

빈생명주기

빈 생명주기 콜백 시장

데이터베이스 커넥션풀, 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애프릴케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면 객체의 초기화와 종료 작업이 필요하다.

간단한게 외부네트워크에 밀 ㅣ연결하는 객체를 하나 생성성한다고 가정했을때 실제로 네트워크를 연결하는것은 아니고 단순히 문자만 출력하도록 했다 이 networkCient 는 애플리케이션 시작 시점에 connect() 를 호출해서 연결을 맺어두어야 하고 애플리케이션이 종료되면 discount() 를 호출해서 연결을 끊어야 한다.

package hello.core.lifecycle;

public class NetworkClient {
    private String url;

    public NetworkClient() {
        //this.url = url;
        System.out.println("생성자 호출 , url = " + url);
        connect();
        call("초기화 연결 메세지 ");
        disConnect();
    }

    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 );
    }

}
package hello.core.lifecycle;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class BeanLifeCycleTest {
    @Test
    void lifeCycleTest() {
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifyCycleConfig.class);
        NetworkClient nc = ac.getBean(NetworkClient.class);
        ac.close();

    }

    @Configuration
    static class LifyCycleConfig{
        @Bean
        public NetworkClient networkClient(){
            NetworkClient nc = new NetworkClient();
            nc.setUrl("http://hello-spring.dev");

            return nc;
        }
    }
}

생성자 부분을 살펴보면 url정보 없이 connect가 호출되는것을 확인할 수 잇다.
너무 당연한 이야기지만 객체를 생성하는 단계에는 url이 없고 객체를 생성한 다음에 외부에서 수정자 중비을 통해서 setUrl()이 홏 ㅜㄹ되어야 url이 존재하게된다.
스프링 빈은 간단하게 다음과 같은 라이프사이클을 가진다.

객체 생성 -> 의존관계 주입

스프링 빈은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료된다. 따라서 초기화 작업은 의존관계 주입이 몯 ㅜ완료되고 난 다음에 호추해야 한다. 그런데 개발자가 의존관계 주입이 모두 완료된 시점 을 어떻게 알 수 있을까?

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주느 ㄴ다양한 기능을 제공 한다. 또한 스프링은 커넽이너가 종료되기 직전에 소멸 콜백을 해준다 따라서 안전하게 종료 작업을 진행할 수 있다.

스프링 빈의 이벤트 라이프 사이클 스프링 컨테이서 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료

  • 초기화 콜백 : 빈이 생성되고 빈의 의존관계 주입이 완료된 후 호출
  • 소멸전 콜백 : 빈이 소멸되기 직전에 호출

참고 : 객체의 생성과 초기화를 분리하자

생성자는 필주 성보를 받고 메모리를 할당해서 객체를 생서하는 책임을 가진다. 반면 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 무거운 동작을 수행한다.

생성자 안에서 무거운 초기화 작업을 하지말고 객체 생성과 초기화 를 나눠야 된다.

스프링이 빈 생명주기 콜백을 지원하는 방법

  • 인터페이스(InitializingBean, DisposableBean)
  • package hello.core.lifecycle; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public class NetworkClient implements InitializingBean, DisposableBean { private String url; public NetworkClient() { //this.url = url; System.out.println("생성자 호출 , url = " + url); connect(); call("초기화 연결 메세지 "); disConnect(); } 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();
      System.out.println("c초기화 연결 메세지 ");
  }

  @Override
  public void destroy() throws Exception {
      disConnect();
  }

}

```

afterPropertiesSet - 의존관계 주입이 끝나면 호출
destroy - 빈 소멸전 호출

  • 설정정보에 초기화 메서드, 종료 메서드 지정

    package hello.core.lifecycle;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    public class BeanLifeCycleTest {
        @Test
        void lifeCycleTest() {
            ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifyCycleConfig.class);
            NetworkClient nc = ac.getBean(NetworkClient.class);
            ac.close();
    
        }
    
        @Configuration
        static class LifyCycleConfig{
            @Bean(initMethod =  "init", destroyMethod = "close")
            public NetworkClient networkClient(){
                NetworkClient nc = new NetworkClient();
                nc.setUrl("http://hello-spring.dev");
    
                return nc;
            }
        }
    }

    설정정보 사용 특징

    • 메서드 이름을 자유롭게 줄 수 있다.
    • 스프링 빈이 스프링 코드에 의존하지 않는다.
    • 코드가 아니라 설정정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드를 적용할 수 있다. 중요**

    종료메서드 추론( @Bean 사용할때 발생 )

    • destroMethod에는 특별한 기능이 있다.
    • 라이브러리는 대부분 close, shutdown 이라는 이름의 종료 메서들르 사용한다.
    • @Bean의 destroMethod에는 메서드에는 디폴트로 (inferred)가 등록되어있다.
    • 추론기능은 close, shutdown라는 이름의 메서드를 자동으로 호출한다.
  • @PostConstruct, @PreDestory 어노테이션 지원

반응형

'Java' 카테고리의 다른 글

[JPA] Hibernate DB 연동  (0) 2022.01.25
[Spring] Prototye N Scope  (0) 2022.01.19
[Spring] Bean 생명주기  (0) 2022.01.08
[Spring] Bean 가공  (1) 2022.01.04
[Spring] 생성자 주입을 선택하는 이유  (0) 2022.01.02