IoC 컨테이너(=스프링 컨테이너)는
Bean(객체)으로 등록된 객체의 생명주기 관리를 위임받아서 관리하고,
객체가 필요한 곳에서 Bean(객체)을 의존성 주입받아서 사용할 수 있게 도와준다.스프링 컨테이너가 생성될 때 객체(Bean)를 생성하고 의존성을 주입하는데,
주입 후 Bean을 초기화하고, 스프링 컨테이너를 종료하기 전에 객체를 소멸시켜야 한다.
Bean의 생명주기(Life Cycle)는 아래와 같다.
스프링 컨테이너 생성 → Bean 생성 → 의존성 주입 → 초기화 콜백(init)
→ Bean 사용 → 소멸 전 콜백(destroy) → 스프링 종료
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;
}
잘 쓰지 않는 방법이다. 아래와 같은 단점들이 있다.
클래스 내부 코드가 아닌, 설정하는 곳(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 |
스프링에서 권장하는 방법이다.
어노테이션을 초기화, 소멸 전 콜백 메서드에 붙여주면 된다.
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이 존재할 수 있는 범위를 뜻한다.
스프링에서 Bean의 범위는 싱글톤이 기본 값이다.
스프링은 아래와 같은 다양한 스코프를 제공한다.
스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.
스프링 컨테이너는 프로토타입 Bean의 생성과 의존 관계 주입까지만 관여하고,
더는 관리하지 않는 매우 짧은 범위의 스코프이다.
따라서 종료 메서드는 호출되지 않는다.
request
: 웹 요청이 들어오고 나갈 때까지 유지되는 스코프session
: 웹 세션이 생성되고 종료될 때까지 유지되는 스코프application
: 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프// 자동 등록
@Scope("singleton") // 생략 가능(기본 값)
@Component // @Service 사용 가능
public class MemberServiceImpl implements MemberService { ... }
// 수동 등록
@Configuration
public class AppConfig {
@Scope("singleton") // 생략 가능
@Bean
public MemberService memberService() {
return new MemberServiceImpl();
}
}
프로토타입 스코프는 Bean이 매 요청마다 생성되게 된다.
상태를 갖게 되는 특징이 있다.
우리는 싱글톤 스코프를 제일 많이 쓴다고 한다.
스코프 종류가 있는 정도로만 알고가자.
또한, 웹 애플리케이션은 보통 다수의 사용자가 동시에 요청을 보내는 환경이다.
이런 상황에서는 Bean이 무상태(stateless) 로 설계되어 있는 것이 좋기 때문에,
대부분의 경우 싱글톤 스코프를 사용하는 것 같다!
빈 생명 주기 콜백
빈 스코프
내배캠 심화 스프링 3주차 강의
내배캠 튜터님들
3주차 강의 듣고 다시 오겠습니다