[SPRING] Bean의 생명주기와 범위

wannabeing·2025년 4월 16일
1

SPRING

목록 보기
7/12
post-thumbnail

IoC 컨테이너(=스프링 컨테이너)는

Bean(객체)으로 등록된 객체의 생명주기 관리를 위임받아서 관리하고,
객체가 필요한 곳에서 Bean(객체)을 의존성 주입받아서 사용할 수 있게 도와준다.

스프링 컨테이너가 생성될 때 객체(Bean)를 생성하고 의존성을 주입하는데,
주입 후 Bean을 초기화하고, 스프링 컨테이너를 종료하기 전에 객체를 소멸시켜야 한다.


Bean 생명주기

Bean의 생명주기(Life Cycle)는 아래와 같다.

스프링 컨테이너 생성 → Bean 생성 → 의존성 주입 → 초기화 콜백(init)
→ Bean 사용 → 소멸 전 콜백(destroy) → 스프링 종료
  • 초기화 콜백(init) : Bean이 생성되고, Bean의 의존성 주입이 완료된 뒤 호출된다.
  • 소멸 전 콜백(destroy) : 스프링이 종료되기 전, Bean이 소멸되기 직전에 호출된다.

3가지 방법으로 Bean의 생명주기를 알아보자.

1. (인터페이스) InitalizingBean, DisposableBean

public class MyBean implements InitializingBean, DisposableBean {

    ...
   
    // InitializingBean 인터페이스의 초기화 메서드
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MyBean 초기화 - afterPropertiesSet() 호출됨");
        System.out.println("data = " + data);
    }

    // DisposableBean 인터페이스의 종료 메서드
    @Override
    public void destroy() throws Exception {
        System.out.println("MyBean 종료 - destroy() 호출됨");
        data = null;
    }

잘 쓰지 않는 방법이다. 아래와 같은 단점들이 있다.

  • 스프링에게 의존적이다.
    다른 라이브러리 코드에 적용할 수 없다.
  • 메서드 이름을 바꿀 수 없다.
    인터페이스이기 때문에 항상 오버라이딩 해야 한다.

2. @Bean, @Configuration

클래스 내부 코드가 아닌, 설정하는 곳(Configuration)에서 지정하기 때문에
내부 코드를 고칠 수 없는 경우에 유용하다.

단, 스프링에서 제공하므로 스프링에 종속적이라는 단점이 있다.

@Getter
@Setter
public class MyBean {

    private String data;

	// 2. 생성자
    public MyBean() {
        System.out.println("Bean 생성자 호출"); // ✅ 2-2. 출력
        System.out.println("data = " + data); // ✅ 2-3. 출력
    }

	// ✅ 2-5. 초기화 콜백 (init)
    public void init() {
        System.out.println("MyBean 초기화 - init() 호출됨");
        System.out.println("data = " + data);
    }
	// ✅ 5. 소멸 전 콜백 (destroy)
    public void close() {
        System.out.println("MyBean 종료 - close() 호출됨");
        data = null;
    }
	
    // ✅ 3. Bean 사용 메서드
    public void doSomething() {
        System.out.println("MyBean 작업 중...");
    }

}
@Configuration
public class AppConfig {
	 // 💡 initMethod = "초기화 메서드명", destroyMethod = "소멸 메서드명")
    @Bean(initMethod = "init", destroyMethod = "close")
    public MyBean myBean() {
        MyBean myBean = new MyBean(); // ✅ 2-1. 생성자 호출
        myBean.setData("Example"); // ✅ 2-4. 의존관계 설정 (Setter 주입)
        return myBean;
    }

}

Bean을 수동등록하기 위해서 @Configuration 어노테이션을 사용한다.

public class BeanLifeCycle {

    public static void main(String[] args) {
        // ✅ 1. IoC 컨테이너 생성 및 설정
        AnnotationConfigApplicationContext context = 
        		new AnnotationConfigApplicationContext(AppConfig.class);

        // ✅ 2. Bean 생성 및 등록
        MyBean myBean = context.getBean(MyBean.class);
        
        // ✅ 3. Bean 사용
        myBean.doSomething();

        // ✅ 4.Bean 소멸 전 필드 출력
        System.out.println(myBean.getData());

        // ✅ 5. 컨테이너 종료 (destroy 호출)
        context.close();

        // Bean 소멸 후 필드
        System.out.println(myBean.getData());
    }

}

순서대로 정리해보자.

단계설명출력 내용
1.Spring 컨테이너 생성 및 설정-
2.Bean 객체 생성 및 등록-
2-1.MyBean 생성자 호출-
2-2.생성자 내 출력Bean 생성자 호출
2-3.생성자 내 출력data = null
2-4.의존관계 설정
setData("Example") 호출
-
2-5.초기화 콜백 init() 실행MyBean 초기화 - init() 호출됨

data = Example
3.Bean 사용: doSomething() 호출MyBean 작업 중...
4.소멸 전 필드 출력Example
5.소멸 전 콜백 close() 실행MyBean 종료 - close() 호출됨
6소멸 후 필드 출력null

3. (권장) @PostConstruct / @PreDestroy ⭐️⭐️⭐️

스프링에서 권장하는 방법이다.
어노테이션을 초기화, 소멸 전 콜백 메서드에 붙여주면 된다.

Java 표준 기술이다.

@Component를 이용하여 Bean을 등록 하는 경우에는
하나의 클래스에서 확인할 수 있다는 장점도 있다.


@Getter
@Setter
public class MyBean {

    private String data;

	// 생성자
    public MyBean() {
        System.out.println("Bean 생성자 호출");
        System.out.println("data = " + data);
    }

	// ✅ 초기화 콜백 (init)
    @PostConstruct
    public void init() {
        System.out.println("MyBean 초기화 - init() 호출됨");
        System.out.println("data = " + data);
    }
	
    // ✅ 소멸 전 콜백 (destroy)
    @PreDestroy
    public void close() {
        System.out.println("MyBean 종료 - close() 호출됨");
        data = null;
    }
	
    // Bean 사용 메서드
    public void doSomething() {
        System.out.println("MyBean 작업 중...");
    }

}
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        MyBean myBean = new MyBean(); // 생성자 호출
        myBean.setData("Example"); // 의존관계 설정 (Setter로 주입)
        return myBean;
    }
}

Bean을 수동등록하기 위해서 @Configuration 어노테이션을 사용한다.

public class BeanLifeCycle {

    public static void main(String[] args) {
        // ✅ 1. IoC 컨테이너 생성 및 설정
        AnnotationConfigApplicationContext context = 
        		new AnnotationConfigApplicationContext(AppConfig.class);

        // ✅ 2. Bean 생성 및 등록
        MyBean myBean = context.getBean(MyBean.class);
        
        // ✅ 3. Bean 사용
        myBean.doSomething();

        // ✅ 4.Bean 소멸 전 필드 출력
        System.out.println(myBean.getData());

        // ✅ 5. 컨테이너 종료 (destroy 호출)
        context.close();

        // Bean 소멸 후 필드
        System.out.println(myBean.getData());
    }

}

실행결과는 위 @Bean 사용 예시와 같다!

이 방법은 내부 코드를 수정할 수 없는 경우에는 사용하지 못한다.
외부 라이브러리에 대한 초기화, 종료 설정을 하려면 위와 같은 @Bean 설정을 이용하자.


Bean의 범위 (Scope)

스프링 컨테이너에서 Bean이 존재할 수 있는 범위를 뜻한다.
스프링에서 Bean의 범위는 싱글톤이 기본 값이다.

스프링은 아래와 같은 다양한 스코프를 제공한다.

1) 싱글톤 (deafult)

스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.

2) 프로토타입

스프링 컨테이너는 프로토타입 Bean의 생성과 의존 관계 주입까지만 관여하고,
더는 관리하지 않는 매우 짧은 범위의 스코프이다.
따라서 종료 메서드는 호출되지 않는다.

3) 웹 관련 스코프

  • request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
  • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
  • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

Bean Scope 설정방법

1) 싱글톤

// 자동 등록
@Scope("singleton") // 생략 가능(기본 값)
@Component // @Service 사용 가능
public class MemberServiceImpl implements MemberService { ... }
// 수동 등록
@Configuration
public class AppConfig {
    @Scope("singleton") // 생략 가능
    @Bean
    public MemberService memberService() {
		    return new MemberServiceImpl();
    }
}

❓ Q. 프토토타입 스코프를 사용할 일이 있을까?

프로토타입 스코프는 Bean이 매 요청마다 생성되게 된다.
상태를 갖게 되는 특징이 있다.

A1. 실제로 쓸 일이 있을까?

  • 멀티쓰레드 환경에서 쓰일 수 있다고는 하나,
  • 튜터님도 실무에서는 거의 사용한 적 없다고 하셨다.
  • 그래서 "이런 종류가 있다" 정도만 알고 있으면 된다는 조언을 받았다.

A2. 사용 예시나 성능상의 이점?

  • 복잡한 계산기가 이전 계산을 기억할 때? 같은 경우가 있다고 하셨다.
  • 예시를 말씀해주시면서, 모두 싱글톤 스코프로도 구현이 가능하다고 하셨다.
  • 성능상의 이점이 있을까? 여쭤봤다.
    • Bean이 계속 새로 생성되면, GC가 열일하게되고,
    • 프록시 객체를 통해 생성되기 때문에
      성능이 더 좋다고 보기 어렵다는 의견이었다.

✅ 정리

우리는 싱글톤 스코프를 제일 많이 쓴다고 한다.
스코프 종류가 있는 정도로만 알고가자.

또한, 웹 애플리케이션은 보통 다수의 사용자가 동시에 요청을 보내는 환경이다.
이런 상황에서는 Bean이 무상태(stateless) 로 설계되어 있는 것이 좋기 때문에,
대부분의 경우 싱글톤 스코프를 사용하는 것 같다!


출처

빈 생명 주기 콜백
빈 스코프
내배캠 심화 스프링 3주차 강의
내배캠 튜터님들

profile
wannabe---ing

2개의 댓글

comment-user-thumbnail
2025년 4월 16일

3주차 강의 듣고 다시 오겠습니다

1개의 답글