빈 라이프사이클과 범위

HeeSeong·2021년 7월 28일
0
post-thumbnail

빈 라이프사이클과 범위


컨테이너 초기화와 종료


스프링 컨테이너는 초기화와 종료라는 라이프사이클을 갖는다.

// 컨테이너 초기화
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppContext.class);

// 컨테이너에서 빈 객체를 구해서 사용
Greeter g = ctx.getBean("greeter", Greeter.clas);
String msg = g.greet("스프링");
System.out.println(msg);

// 컨테이너 종료
ctx.close();

위 코드를 보면 AnnotationConfigApplicationContext의 생성자를 이용해서 컨텍스트 객체를 생성하는데 이 시점에 스프링 컨테이너를 초기화한다. 스프링 컨테이너는 설정 클래스에서 정보를 읽어와 알맞은 빈 객체를 생성하고 각 빈을 연결하는 작업을 수행한다.

컨테이너 초기화가 완료되면 컨테이너를 사용할 수 있다. 사용한다는 것은 getBean 같은 메서드를 이용해서 빈 객체를 구한다는 것을 뜻한다.

컨테이너 사용이 끝나면 컨테이너를 종료한다. close() 메서드는 AbstractApplicationContext 클래스에 정의되어 있다. 스프링 컨테이너의 라이프사이클에 따라 빈 객체도 자연스럽게 생성과 소멸이라는 라이프사이클을 갖는다.


스프링 빈 객체의 라이프 사이클


스프링 컨테이너는 빈 객체의 라이프사이클을 관리한다.

객체 생성 - 의존 설정 - 초기화 - 소멸

스프링 컨테이너를 초기화할 때 스프링 컨테이너는 가장 먼저 빈 객체를 생성하고 의존을 설정한다. 의존 자동 주입을 통한 의존 설정이 이 시점에 수행된다. 모든 의존 설정이 완료되면 빈 객체의 초기화를 수행한다. 빈 객체를 초기화하기 위해 스프링은 빈 객체의 지정된 메서드를 호출한다. 스프링 컨테이너를 종료하면 스프링 컨테이너는 빈 객체의 소멸을 처리한다. 이때에도 지정한 메서드를 호출한다.


  • Client.java
public class Client implements InitializingBean, DisposableBean {

	private String host;

	public void setHost(String host) {
		this.host = host;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("Client.afterPropertiesSet() 실행");
	}

	public void send() {
		System.out.println("Client.send() to " + host);
	}

	@Override
	public void destroy() throws Exception {
		System.out.println("Client.destroy() 실행");
	}
}

빈 객체가 InitializingBean 인터페이스를 구현하면 스프링 컨테이너는 초기화 과정에서 빈 객체의 afterPropertiesSet() 메서드를 실행한다.

빈 객체가 DisposableBean 인터페이스를 구현하면 스프링 컨테이너는 소멸 과정에서 빈 객체의 destroy() 메서드를 실행한다.


  • AppCtx.java
@Configuration
public class AppCtx {

	@Bean
	public Client client() {
		Client client = new Client();
		client.setHost("host");
		return client;
	}
}

콘솔에 출력된 메세지의 순서를 보면 먼저 afterPropertiesSet() 메서드를 실행하낟. 즉 스프링 컨테이너는 빈 객체 생성을 마무리한 뒤에 초기화 메서드를 실행한다. 가장 마지막에 destroy() 메서드를 실행한다. 이 메서드는 스프링 컨테이너를 종료하면 호출된다는 것을 알 수 있다.


빈 객체의 초기화와 소멸 : 커스텀 메서드


직접 구현한 클래스가 아닌 외부에서 제공받은 클래스를 스프링 빈 객체로 설정하고 싶을 때도 있다. 이 경우 소스 코드를 받지 않았다면 두 인터페이스를 구현하도록 수정할 수 없다. 이렇게 InitializingBean, Disposable 인터페이스를 구현할 수 없거나, 이 두 인터페이스를 사용하고 싶지 않은 경우에 스프링 설정에서 직접 메서드를 지정할 수 있다.

방법은 @Bean 태그에서 initMethod 속성과 destroyMethod 속성을 사용해서 초기화 메서드와 소멸 메서드의 이름을 지정하면 된다.


  • Client2.java
public class Client2 {

	private String host;

	public void setHost(String host) {
		this.host = host;
	}

	public void connect() {
		System.out.println("Client2.connect() 실행");
	}

	public void send() {
		System.out.println("Client2.send() to " + host);
	}

	public void close() {
		System.out.println("Client2.close() 실행");
	}

}

Client2 클래스를 빈으로 사용하려면 초기화 과정에서 connect() 메서드를 실행하고 소멸 과정에서 close() 메서드를 실행해야 한다면 @Bean 애노테이션의 initMethod, destroyMethod 속성에 초기화와 소멸 과정에서 사용할 메서드 이름을 지정해주면 된다.


	@Bean(initMethod = "connect", destroyMethod = "close")
	public Client2 client2() {
		Client2 client = new Client2();
		client.setHost("host");
		return client;
	}

빈 객체의 생성과 관리 범위


스프링 컨테이너는 빈 객체를 한 개만 생성한다고 했따. 이렇게 한 식별자에 대해 한 개의 객체만 존재하는 빈은 싱글톤(singleton) 범위를 갖는다. 사용빈도가 낮긴 하지만 프로토타입 범위의 빈을 설정할 수도 있다.

빈의 범위를 프로토타입으로 지정하면 빈 객체를 구할 때마다 매번 새로운 객체를 생성한다.

특정 빈을 프로토타입 범위로 지정하려면 다음과 같이 값으로 "prototype"을 갖는 @Scope 애노테이션을 @Bean 애노테이션과 함께 사용하면 된다.


@Configuration
public class AppCtxWithPrototype {

	@Bean
	@Scope("prototype")
        // @Scope("singleton") 가능
	public Client client() {
		Client client = new Client();
		client.setHost("host");
		return client;
	}
}

프로토타입 범위를 갖는 빈은 완전한 라이프사이클을 따르지 않는다는 점에 주의해야 한다. 스프링 컨테이너는 프로토타입의 빈 객체를 생성하고 프로퍼티 설정, 초기화 작업까지는 수행하지만, 컨테이너 종료시에 생성한 프로토타입 빈 객체의 소멸 메서드를 실행하지는 않는다. 프로토타입 범위의 빈을 사용할 때에는 빈 객체의 소멸 처리를 직접 해야 한다.

profile
끊임없이 성장하고 싶은 개발자

0개의 댓글