포스트

Bean객체와 생명주기를 알아보자

Bean

자바에서의 Bean

자바에서 Bean객체란 POJO의 한 형태인 javaBean 객체를 의미하며, 정의는 아래와 같다

데이터를 저장하기 구조체로 자바 빈 규약이라는 것을 따르는 구조체이다

자바 빈 규약 중 중요한 몇가지는 아래와 같다.

  • private하게 프로퍼티를 선언하고 getter/setter로만 데이터를 접근한다.
  • getter 와 setter는 접근자가 public으로 선언되어야 한다.
  • 인수(argument)가 없는 기본 생성자가 있다.
  • 직렬화 되어 있어야 한다. (선택사항)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class JavaBean {
  
	private String id;
	private Integer count;

	public JavaBean(){}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Integer getCount() {
		return count;
	}

	public void setCount(Integer count) {
		this.count = count;
	}
}

스프링에서의 Bean

스프링 프레임워크에서 말하는 Bean 은 조금 더 확장된 의미를 가진다.
스프링에서 Bean 이라고 할 때는, 스프링 IoC컨테이너에 의해 관리되는 객체를 의미한다.

  • 스프링 IoC 컨테이너에 의해 생성되고 관리되는 객체
  • 자바에서처럼 new Object(); 로 생성하지 않는다
  • 각각의 Bean들 끼리는 서로를 편리하게 의존(사용)할 수 있음

위 사진 처럼, 실제로 Spring Application Context에서 Bean객체들이 리스트로 등록이 되어있다.

(앞선 Application 선언 부분에 ListableBeanFactory를 상속받은 부분 참조)

각 객체 A, B, C를 빈으로 등록한다는 것은 단순히 우리가 만들 클래스를 그대로 쓴다는 의미가 아니다.

Spring ApplicationContext는 우리가 만든 클래스에 어노테이션으로 정의한 설정들을 입혀준다.

예를들어 @Controller이면 컨트롤러역할과 관련된 설정을, @Service면 서비스 계층과 관련된 설정을, 아니면 그냥 @Component면 기본 설정 등등을 적용해준다.

또한 각 설정한 어노테이션을 기반으로 객체간의 의존성도 판단해서 각 의존성에 맞게 객체 주입을 해준다

그리고 Scope에 따라 (일반적으로 싱글톤 타입으로) 인스턴스로 실체화 된다.

Bean의 등록

  • 과거에는 xml로 설정을 따로 관리하여 등록(불편하고 귀찮음)
  • 현재는 annotation 기반으로 Bean 등록
    • @Bean, @Controller, @Service

빈 정의(Bean Definition)

스프링에서 빈(bean)을 생성하고 관리하는 데 필요한 메타데이터

  • Class 경로

  • Bean의 이름
    • 기본적으로는 원본 Class 이름에서 첫 문자만 소문자로 변경 → accountService, userDao
    • 원하는 경우 커스텀 가능
  • Scope : Bean을 생성하는 규칙
    • singleton : 컨테이너에 단일로 생성
    • prototype: 작업 시마다 Bean을 새로 생성하고 싶을 경우
    • request: http 요청마다 새롭게 Bean을 생성하고 싶은 경우
  • 의존성 정보
    • 해당 Bean이 다른 Bean에 의존하는 경우, 이 의존성도 빈정의의 포함
  • 생명주기 콜백
    • 빈의 생명주기 동안 호출될 메소드

Bean LifeCycle callback(빈 생명주기 콜백함수)

  • callback : 어떤 이벤트가 발생하는 경우 호출되는 메서드

  • Bean lifecycle callback
    • Bean을 생성하고 초기화하고 파괴하는 등 특정 시점에 호출되도록 정의된 메서드
  • 주로 많이 사용되는 콜백
    • @PostConstruct : 빈 생성 시점에 필요한 작업을 수행
    • @PreDestroy : 빈 파괴(주로 어플리케이션 종료) 시점에 필요한 작업을 수행

대표적으로 네트워크 커넥션에 사용한다.

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
import javax.annotation.PreDestroy;

@Component
public class NetworkClient {
  private String url;

  public NetworkClient() {
    System.out.println("생성자 호출, url = " + this.url);
  }

  @PostConstruct
  //생성시에 호출되는 함수
  public void init() {
    System.out.println("NetworkClient.init");
    connect();
    call("초기화 연결 메시지");
  }

  @PreDestroy
  //빈 파괴시점에 호출되는 함수
  public void close() {
    System.out.println("NetworkClient.close");
    disconnect();
  }

  public void disconnect() {
    System.out.println("disconnect from " + url);
  }
}

한 클래스가 빈에 등록되고 사용되어 끝날때 까지의 과정을 말해보자

앞서 살펴본 내용을 바탕으로, @Component 어노테이션을 사용해 클래스를 빈(Bean)으로 등록후의 사용, 소멸까지의 LifeCycle을 살펴보겠다.

1. 컴포넌트 스캔

스프링 애플리케이션을 시작할 때 @ComponentScan을 사용하여 설정된 패키지 내에서 @Component 어노테이션이 적용된 클래스를 자동으로 검색한다.

이것은 또한 @Service, @Repository, @Controller, @Repository 같은 다른 스테레오타입 어노테이션을 포함한다.

2. 빈 정의 생성

발견된 각 클래스에 대해 빈 정의(Bean Definition)가 생성된다. 빈 정의는 빈의 생성 방법, 스코프, 생명주기 콜백 등을 포함하는 메타데이터이다.

3. 스코프 결정

빈의 스코프가 결정된다. @Scope 어노테이션이 사용되지 않았다면, 기본적으로 singleton 스코프가 적용된다.

4. 의존성 주입 설정

빈 정의에 따라, 스프링 컨테이너는 클래스 내에서 @Autowired, @Resource, @Inject 등의 어노테이션을 통해 의존성 주입이 필요한 생성자, 필드, 메서드를 식별한다.

5. 빈 인스턴스 생성

설정된 스코프에 따라 스프링 컨테이너는 빈 인스턴스를 생성한다. singleton 스코프는 애플리케이션 컨텍스트 내에서 단 하나의 인스턴스만 생성되며, prototype 스코프는 요청마다 새로운 인스턴스가 생성된다.

6. 의존성 주입

빈 인스턴스 생성 직후, 컨테이너는 식별된 의존성 주입 포인트에 필요한 빈을 주입한다. 이는 생성자 주입, setter 주입 또는 field 주입 방식을 통해 이루어질 수 있다.

7. 초기화 콜백

의존성 주입이 완료된 후, @PostConstruct 어노테이션으로 표시된 메서드가 호출되어 빈의 초기화를 수행한다. 또한 InitializingBean 인터페이스의 afterPropertiesSet() 메소드를 구현하여 사용할 수도 있다.

8. 애플리케이션 컨텍스트에 빈 등록

초기화가 완료된 빈은 애플리케이션 컨텍스트에 등록되어 애플리케이션 전반에서 사용할 준비가 된다.

9. 빈 사용

애플리케이션의 다양한 부분에서 빈이 필요할 때, 스프링 컨테이너는 이 빈들을 제공한다.

10. 소멸 전 콜백

애플리케이션이 종료되기 전, @PreDestroy 어노테이션으로 표시된 메서드가 호출되어

빈의 소멸 과정을 준비한다. 또한 DisposableBean 인터페이스의 destroy() 메소드를 구현하여 사용할 수 있다.

11. 스프링 종료

애플리케이션 종료 시, 스프링 컨테이너는 모든 singleton 스코프 빈의 소멸 과정을 관리하며 필요한 자원을 정리한다.

음? 그럼 싱글톤 말고 프로토타입 스코프의 빈 객체들의 소멸관리는?

다음은 스프링 공식문서에서 prototype-bean 의 Life-cycle에 대한 설명중 일부이다.

Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype beans hold

해석 하면 아래와 같다

따라서 초기화 수명 주기 콜백 메서드는 범위에 관계없이 모든 객체에서 호출되지만, 프로토타입의 경우 구성된 소멸 수명 주기 콜백은 호출되지 않습니다. 클라이언트 코드는 프로토타입 범위의 객체를 정리하고 프로토타입 빈이 보유하고 있는 값비싼 리소스를 해제해야 합니다.

즉 프로토타입 빈이 소멸 시에는 @PreDestroy로 선언한 소멸콜백함수가 호출되지 않는다는 소리다.

따라서, 프로토타입 빈을 사용할 때는 이를 생성하고 관리하는 코드에서 빈의 소멸과 자원 해제 과정을 구현해야한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.