본문 바로가기
language/java

객체 생명주기와 Spring Bean

by 죄니안죄니 2026. 6. 17.
반응형

여기서부터는 Spring에서 가장 중요한 개념 중 하나Bean 생명주기(Lifecycle) 를 다룬다.

많은 개발자가

 
new UserService();
 

정도만 생각하지만,

Spring에서는 객체가 만들어지고 사라질 때까지 여러 단계의 생명주기를 거친다.

그리고 이 과정을 이해해야

  • @PostConstruct
  • @PreDestroy
  • BeanPostProcessor
  • @Transactional
  • @Async
  • AOP

가 언제 동작하는지 자연스럽게 이해할 수 있다.


객체 생명주기와 Spring Bean

Spring Bean은 생성만 되는 것이 아니라 평생 관리된다.

이전 글에서는

@ComponentScan
        ↓
BeanDefinition 생성
        ↓
Reflection 객체 생성
        ↓
DI
        ↓
BeanPostProcessor
        ↓
Singleton Cache 저장
 

까지 살펴봤다.

그런데 여기서 하나의 질문이 생긴다.

Spring은 Bean을 언제 생성하고, 언제 사용할 수 있으며, 언제 제거할까?

이 질문의 답이 바로 Bean Lifecycle(생명주기) 이다.


일반 Java 객체의 생명주기

우리가 직접 객체를 생성하면 매우 단순하다.

 
UserService service =
        new UserService();

service.save();
 

흐름은

new

↓

생성자 실행

↓

사용

↓

참조 제거

↓

GC
 

이다.


객체가 언제 사라질지는

GC가 결정한다.

개발자가 제어할 수 없다.


Spring Bean은 다르다.

Spring은

객체를 단순히 생성하는 것이 아니라

생명주기 전체를 관리한다.


Bean 생성

↓

DI

↓

초기화

↓

사용

↓

소멸
 

이 모든 과정이 Spring Container 안에서 이루어진다.


전체 생명주기

가장 먼저 전체 그림을 보자.

Application Start

        │

        ▼

Reflection으로 객체 생성

        │

        ▼

생성자(Constructor)

        │

        ▼

Dependency Injection

        │

        ▼

BeanPostProcessor(before)

        │

        ▼

@PostConstruct

        │

        ▼

InitializingBean.afterPropertiesSet()

        │

        ▼

Custom initMethod

        │

        ▼

BeanPostProcessor(after)

        │

        ▼

사용 가능(Ready)

==============================

Application Shutdown

        │

        ▼

@PreDestroy

        │

        ▼

DisposableBean.destroy()

        │

        ▼

Custom destroyMethod
 

1단계

생성자(Constructor)

Spring은 Reflection으로 객체를 생성한다.

 
@Service
public class UserService {

    public UserService() {

        System.out.println("Constructor");

    }

}
 

실행

Constructor
 

이 시점에는

아직 DI가 끝나지 않았다.


즉,

 
@Service
public class OrderService {

    @Autowired
    private UserService userService;

    public OrderService() {

        userService.save();

    }

}
 

여기서는

userService == null
 

일 수 있다.


생성자에서

다른 Bean에 의존하는 로직은 작성하지 않는 것이 좋다.


2단계

Dependency Injection

객체가 만들어지면

Spring은 필요한 Bean을 주입한다.


 
@Service
public class OrderService {

    private final UserService userService;

    public OrderService(UserService userService) {

        this.userService = userService;

    }

}
 

이 시점부터

userService

↓

사용 가능
 

이다.


3단계

BeanPostProcessor(before)

Spring 내부에는

BeanPostProcessor라는 확장 포인트가 있다.


Bean이 생성될 때마다

자동으로 실행된다.


Bean 생성

↓

postProcessBeforeInitialization()
 

Spring 내부의 수많은 기능이

여기서 시작된다.


4단계

@PostConstruct

가장 많이 사용하는 초기화 방식이다.

 
@Service
public class UserService {

    @PostConstruct
    public void init() {

        System.out.println("Init");

    }

}
 

실행 순서

Constructor

↓

DI

↓

@PostConstruct
 

즉,

이 시점에는

모든 의존성이 이미 주입되어 있다.


그래서

 
@PostConstruct
public void init() {

    userService.load();

}
 

처럼

다른 Bean을 안전하게 사용할 수 있다.


왜 생성자가 아니라 @PostConstruct를 사용할까?

생성자

객체만 생성

↓

DI 아직 안 됨
 

@PostConstruct

DI 완료

↓

초기화 수행
 

초기 데이터 로딩,

캐시 생성,

외부 연결 준비 등에 적합하다.


5단계

InitializingBean

Spring이 제공하는 인터페이스이다.

 
public class UserService

implements InitializingBean {

    @Override
    public void afterPropertiesSet() {

        System.out.println("Init");

    }

}
 

@PostConstruct와 거의 같은 역할이다.


하지만

Spring 인터페이스에 의존하게 되므로

최근에는

 
@PostConstruct
 

를 더 많이 사용한다.


6단계

initMethod

Configuration에서도 초기화할 수 있다.

 
@Bean(initMethod = "init")
public UserService userService() {

    return new UserService();

}
 

 
public void init() {

}
 

가 자동으로 호출된다.


7단계

BeanPostProcessor(after)

초기화가 끝나면

다시 BeanPostProcessor가 실행된다.


postProcessAfterInitialization()
 

이 단계에서

가장 중요한 일이 발생한다.


@Transactional

 
@Service
public class UserService {

    @Transactional
    public void save() {

    }

}
 

Spring은

UserService

↓

Transaction Proxy

↓

Bean Container 저장
 

으로 변경한다.


즉,

실제로 Container에 저장되는 것은

원본 객체가 아니라

Proxy일 수도 있다.


사용 단계

이제 Bean은

Ready 상태이다.


 
@Autowired
private UserService userService;
 

Controller

Service

Repository


모든 곳에서

동일한 Singleton Bean을 사용한다.


종료 단계

Application 종료

Bean 소멸

@PreDestroy 실행


 
@PreDestroy
public void close() {

    System.out.println("Close");

}
 

대표적인 사용 예

Connection 종료

ThreadPool 종료

파일 저장

캐시 정리
 

DisposableBean

 
implements DisposableBean
 

 
@Override
public void destroy() {

}
 

역시

Spring 인터페이스에 의존하기 때문에

최근에는

 
@PreDestroy
 

가 더 많이 사용된다.


Scope에 따라 생명주기가 달라진다.

Singleton

Application Start

↓

생성

↓

계속 사용

↓

Application 종료

↓

소멸
 

Prototype

요청

↓

생성

↓

사용

↓

Spring 관리 종료
 

Spring은

생성까지만 하고

소멸은 관리하지 않는다.


Request Scope

HTTP 요청

↓

Bean 생성

↓

Controller

↓

Service

↓

응답 완료

↓

Bean 소멸
 

웹 환경에서만 사용된다.


BeanPostProcessor가 중요한 이유

많은 Spring 기능은

여기서 구현된다.

Bean 생성

↓

BeanPostProcessor

↓

Proxy 생성

↓

Container 저장
 

대표적으로

  • @Transactional
  • @Async
  • @Cacheable
  • AOP

가 모두 이 과정을 거친다.


그림으로 전체 정리

Reflection 객체 생성

        │

        ▼

Constructor

        │

        ▼

Dependency Injection

        │

        ▼

BeanPostProcessor(before)

        │

        ▼

@PostConstruct

        │

        ▼

InitializingBean

        │

        ▼

initMethod

        │

        ▼

BeanPostProcessor(after)

        │

        ▼

Proxy 생성 가능

        │

        ▼

Ready

==============================

Application 종료

        │

        ▼

@PreDestroy

        │

        ▼

DisposableBean

        │

        ▼

destroyMethod
 

실무에서는 이렇게 기억하면 된다.

초기화

생성자

↓

DI

↓

@PostConstruct

↓

사용 시작
 

종료

Application 종료

↓

@PreDestroy

↓

자원 정리
 

면접 포인트

Spring Bean 생명주기는?

생성

↓

DI

↓

초기화

↓

사용

↓

소멸
 

생성자에서 다른 Bean을 사용하면 안 되는 이유는?

DI가 아직 완료되지 않았기 때문이다.


@PostConstruct는 언제 실행되는가?

모든 의존성이 주입(DI 완료)된 이후, Bean이 사용되기 직전에 실행된다.


BeanPostProcessor는 무엇인가?

Bean 생성 후 전후에 개입할 수 있는 확장 포인트이며, Spring의 Proxy 기반 기능(@Transactional, @Async, AOP 등)이 이 메커니즘을 이용한다.


다음 글

다음은 Dynamic Proxy와 Spring AOP이다.

이 글에서는 지금까지 배운

Reflection
        ↓
Bean 생성
        ↓
BeanPostProcessor
        ↓
Proxy 생성
 

이 실제로 AOP가 되는 과정을 코드와 그림으로 연결해서 설명하면 Spring 내부 구조가 하나의 흐름으로 완성된다.

반응형

'language > java' 카테고리의 다른 글

JDK Proxy vs CGLIB in Spring  (0) 2026.06.17
Dynamic Proxy와 Spring AOP  (0) 2026.06.17
Bean 생성 과정  (0) 2026.06.17
Reflection과 Spring DI  (0) 2026.06.17
Virtual Thread(Java 21)  (0) 2026.06.17

댓글