Home [spring] 스프링빈 생명주기 콜백
Post
Cancel

[spring] 스프링빈 생명주기 콜백





콜백이란?



우선 콜백이란

프로그래밍에서 콜백(callback) 또는 콜백 함수(callback function)는 다른 코드의 인수로서 넘겨주는 실행 가능한 파일 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.(위키백과)


즉 조건에 따라 즉시 실행시키거나 실행을 미룰 수 있는 함수이다.




빈 생명주기 콜백?



우선 스프링빈의 라이프 사이클을 알아보자.



스프링 빈 이벤트 Life-Cycle

스프링 컨테이너 생성 → 스프링 빈 등록(객체 생성) → 의존관계 주입 → 초기화 콜백 메서드 호출 → 빈 사용 → 소멸전 콜백 메서드 호출 → 종료



위의 라이프사이클을 보면 스프링 빈은 객체를 생성하고 의존관계 주입이 들어간다.


그렇다면 생성자 초기화같은 경우는 생성과 함께 초기화를 진행하는데 의존관계가 주입되지 않은 상태로 초기화를 진행할것이다.

예를 들어 DB커넥션이나 소켓 연결과 같이 애플리케이션 시작(생성자 초기화) 시점에 필요한 연결을 미리 해두고, 애플리케이션이 종료시점에 연결을 종료하는 작업을 진행하려면 객체의 초기화와 종료 작업이 필요할 것이다.




NetworkClient.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class NetworkClient{
    public String url;

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

    // 서비스 시작시 호출
    public void connect() {
        System.out.println("connect : " + url);
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void call(String message) {
        System.out.println("call : " + url + ", message : " + message);
    }

    // 종료시점 호출
    public void disconnect(){
        System.out.println("close : "+url);
    }


실제 네트워크를 연결하는 것이 아닌 단순 문자열을 출력하는 객체이다.

NetworkClient 의 객체 생성과 함께 connect() 를 호출하여 네트워크와 연결한다는 가정의 코드이다.




LifeCycle.class

1
2
3
4
5
6
7
8
9
10
@Configuration
    static class lifeCycle{

        @Bean
        public NetworkClient networkClient(){
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://spring_tutorial");
            return networkClient;
        }
	   }


NetworkClient 객체를 스프링빈으로 등록해주었다.

NetworkClient 를 생성하고 외부에서 setter 주입자로 url을 주입해준다.




BeanLifeCycleTest.class

1
2
3
4
5
6
7
8
9
public class beanLifeCycleTest {

    @Test
    void lifeCycleTest(){
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(lifeCycle.class);
        NetworkClient bean = ac.getBean(NetworkClient.class);
        ac.close();
    }
}


스프링 컨테이너에서 networkClient 빈을 조회만 하고 스프링 컨테이너를 종료하는 테스트이다.



ac.close()ConfigurableApplicationContext 을 참조해야 호출할 수 있는 메서드이다.

ConfigurableApplicationContext.interface

1
2
3
4
5
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

@Override
	void close();
}




이제 테스트 결과를 보자

Untitled



url정보가 없이 connect()가 호출된게 보인다.

객체의 생성 시점에 url정보가 없고 외부에서 setter 를 통해 url정보를 주입해주어야 url정보가 들어간다.

이렇게 생성자 초기화를 사용하면 의존관계 주입이 이루어지지 않고 값을 초기화 하기때문에 위와 같은 문제가 발생 할 수도있다.

그래서 스프링은 의존관계 주입이 끝나면 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려주며 스프링 컨테이너가 종료 되기 직전에 콜백 메서드를 통해 종료 시점을 알려준다.





객체의 생성과 초기화를 분리


생성자와 초기화를 분리하자

생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.

초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는등 무거운 동작을 수행한다.

따라서 생성자 안에서 무거운 초기화 작업을 함께 하는 것 보다는 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다.





3가지 콜백 메서드



스프링에선 크게 3가지로 콜백 메서드를 지원해준다.




1. InitializingBean, DisposableBean 인터페이스 구현



1
2
3
4
5
6
7
8
9
10
11
public interface InitializingBean {

	void afterPropertiesSet() throws Exception;
		// 의존관계가 끝나고 수행해야할 구문
}

public interface DisposableBean {

	void destroy() throws Exception;

}


InitializingBean은 afterPropertiesSet()을 초기화 메서드로 지원한다.

의존관계가 끝나고 수행해야할 구문을 넣어준다.

DisposableBean은 destroy()를 종료 메서드로 지원한다.

bean종료전에 자원반납(close)같은 메서드를 넣어준다.


NetworkClient에 적용해보자.


NetworkClient.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class NetworkClient implements InitializingBean, DisposableBean {
    public String url;

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

    // 서비스 시작시 호출
    public void connect() {
        System.out.println("connect : " + url);
    }

    public void setUrl(String url) {
        this.url = 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();
		}



테스트 결과

Untitled

결과를 보면 url 주입완료 후에 초기화 콜백 메서드가 호출된 것이 보이고 스프링 컨테이너가 종료가 호출되자 소멸 콜백 메서드도 호출된것이 보인다.



InitializingBean, DisposableBean의 단점

  • 스프링 전용 인터페이스 이기 때문에 적용한 코드가 스프링 전용 인터페이스에 의존한다.
  • 초기화, 소멸 메서드의 이름을 변경할 수 없다.
  • 외부 라이브러리에 적용할 수 없다.





2. 빈에 초기화, 소멸 메서드 지정


설정 정보@Bean(initMethod = “init”, destoryMethod = “close”) 초기화, 소멸 메서드를 지정할 수있다.

LifeCycle.class

1
2
3
4
5
6
7
8
9
10
@Configuration
    static class lifeCycle{

        @Bean(initMethod = "init", destoryMethod = "close")
        public NetworkClient networkClient(){
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://spring_tutorial");
            return networkClient;
        }
	   }


테스트 결과

Untitled



장점

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



단점

  • Bean 지정 시 initMethod와 destroyMethod를 붙여줘야 하는 번거로움이 있다.

destroyMethod의 특별한 기능 “종료 메서드 추론

라이브러리의 대부분은 종료메서드를 close, shutdown이라는 이름을 사용한다.

destoryMethod는 기본값이 inferred(추론)인데 말그대로 이름으로 종료메서드를 추론해서 호출한다.

따라서 종료메서드는 따로 적어주지 않아도 잘 동작한다.





@PostConstruct, @PreDestory


초기화와 종료 메서드에 애너테이션을 붙여주기만 하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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();
    }
}



테스트 결과

Untitled

@PostConstruct와 @PreDestory 두 애너테이션만 붙이면 편리하게 초기화와 종료 콜백 메서드를 실행 할 수 있다.



특징

  • 최신 스프링에서 가장 권장하는 방법이다.
  • 패키지를 보면 package javax.annotation이다. 즉, 스프링에 종속된 기술이 아니라 JSR-250 자바 표준기술이다.
  • 애너테이션이기 때문에 컴포넌트 스캔과 잘 어울린다.



단점

  • 외부 라이브러리에 적용하지 못한다. 외부 라이브러리에 초기화, 종료 콜백 메서드를 해야한다면 @Bean(initMethod = “”, destoryMethod = “”)방법을 사용 하자










오늘은 스프링 빈의 생명주기와 초기화, 종료 메서드 3가지에 대해 알아보았다.

초반에 생성자 초기화와 생성자 주입 개념이 헷갈려 시간이 좀 오래 걸렸다.

다음은 스프링 빈 스코프에 대해 알아보자.

출처

출처:https://www.inflearn.com/course/스프링-핵심-원리-기본편

This post is licensed under CC BY 4.0 by the author.

[spring] 의존 관계 자동 주입

[jpa] jpa란