1️⃣ 객체 생성(Instantiation)
<bean> 태그, Java 기반 설정에서는 @Bean 또는 @Component 로 등록한 객체가 대상이 됨@Component
public class MyBean {
public MyBean() {
System.out.println("1. Bean 객체 생성");
}
}singleton이므로 스프링 컨테이너가 시작될 때 Bean이 미리 생성됨2️⃣ 의존성 주입(Dependency Injection)
@Autowired를 통해 필요한 의존성을 주입 받음@Autowired가 붙은 필드, 생성자, 또는 Setter 메서드를 보고 알맞은 Bean을 찾아서 주입@Component
public class MyService {
@Autowired
private MyBean myBean; // 필드 인젝션
public void doSomething() {
myBean.someMethod();
}
}@Autowired를 필드에 직접 붙여서 의존성을 주입하는 방식@Component
public class MyService {
private MyBean myBean;
@Autowired
public void setMyBean(MyBean myBean) { // Setter 인젝션
this.myBean = myBean;
}
public void doSomething() {
myBean.someMethod();
}
}setMyBean()을 호출해서 언제든지 값을 바꿀 수 있음 → 불변 객체를 만들기 어려움@Component
public class MyService {
private final MyBean myBean;
@Autowired
public MyService(MyBean myBean) {
this.myBean = myBean;
System.out.println("2. 의존성 주입");
}
}@Autowired를 생성자에 붙여서 의존성을 주입하는 방식final을 사용해 불변성을 유지할 수 있고, 테스트가 용이하므로 가장 많이 사용final을 붙일 수 없고, 생성자 인젝션에는 붙일 수 있는 이유final 필드는 딱 한 번만 초기화 가능(선언과 동시에 하거나 생성자에서 가능 ⇒ 이 외에는 컴파일 오류 발생)생성자 실행 → 객체 생성 완료 → Spring이 @Autowired로 필드에 값을 집어넣음(리플렉션 사용)final 키워드 사용 불가private도 조작 가능@Autowired 생략 가능@Component
public class MyService {
private final MyBean myBean;
public MyService(MyBean myBean) { // @Autowired 생략 가능
this.myBean = myBean;
}
}@Component
@RequiredArgsConstructor // 필수(final) 필드만 가지고 생성자 만들어주는 역할
public class MyService {
private final MyBean myBean;
}@RequiredArgsConstructor를 사용하면 생성자 코드도 생략 가능 ⇒ final이나 @NonNull이 붙은 필드만 골라서 생성자를 자동으로 만들어주는 롬복 어노테이션3️⃣ 초기화(Initialization)
@PostConstruct 또는 InitializingBean 인터페이스를 사용할 수 있음@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("3. 초기화 로직");
}
}4️⃣ 사용(Usage)
5️⃣ 소멸(Destruction)
@PreDestroy 또는 DisposableBean 인터페이스를 사용해서 정리 작업을 수행할 수 있음@Component
public class MyBean {
@PreDestroy
public void destroy() {
System.out.println("4. 소멸 단계 - 리소스 정리");
}
} 1️⃣ @PostConstruct, @PreDestroy 사용
@PostConstruct(초기화)와 @PreDestroy(소멸) 메서드를 정의해서 사용@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("초기화 실행");
}
@PreDestroy
public void cleanup() {
System.out.println("소멸 실행");
}
} 2️⃣ InitializingBean, DisposableBean 인터페이스 구현
InitializingBean의 afterPropertiesSet() 메서드에서 초기화 로직을 작성할 수 있음DisposableBean의 destroy() 메서드에서 소멸 시 실행할 로직을 작성할 수 있음@Component
public class MyBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
System.out.println("초기화 실행!");
}
@Override
public void destroy() {
System.out.println("소멸 실행");
}
} 3️⃣ Bean 설정 파일에서 initMethod, destroyMethod 지정
@Bean 어노테이션을 사용할 때, initMethod와 destroyMethod 속성을 지정할 수도 있음@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
}public class MyBean {
public void init() {
System.out.println("초기화 실행");
}
public void cleanup() {
System.out.println("소멸 실행");
}
} | 스코프 | 설명 |
|---|---|
singleton | 컨테이너 내에서 Bean이 한 개만 생성됨 ⇒ 기본값 |
prototype | 매번 새로운 Bean 인스턴스를 생성함 |
request | HTTP 요청마다 새로운 Bean을 생성 |
session | HTTP 세션마다 새로운 Bean을 생성 |
1️⃣ Singleton과 Prototype의 생명주기 차이
@Component
@Scope("prototype")
public class MyPrototypeBean {
@PostConstruct
public void init() {
System.out.println("Prototype Bean 초기화");
}
@PreDestroy
public void cleanup() {
System.out.println("Prototype Bean 소멸");
}
}
singleton 스코프의 경우 컨테이너가 시작될 때 Bean이 생성되지만, prototype 스코프는 매번 새로운 객체가 생성됨prototype 스코프의 Bean은 컨테이너가 직접 관리하지 않기 때문에 @PreDestroy가 실행되지 않음@Scope("prototype")
public class MyPrototypeBean {
public void cleanup() {
System.out.println("수동으로 정리 작업 실행");
}
}prototype 빈이 싱글톤 빈에 의존성 주입이 되어 있으면 매번 새로운 빈이 생성되지 않음 ⇒ ObjectProvider 사용하면 매번 새로운 prototype 빈을 주입 받을 수 있음@Component
public class SingletonBean {
@Autowired
private ObjectProvider<ProtoBean> protoBeanProvider;
public void doWork() {
ProtoBean protoBean = protoBeanProvider.getObject(); // 사용할 때마다 새로 생성
System.out.println(protoBean);
}
}ObjectProvider는 Spring에서 제공하는 지연 조회(필요할 때만 꺼내서 쓰는) 도구로, prototype 빈처럼 매번 새로운 객체가 필요할 때 유용@Component
public class DatabaseConnection {
private Connection connection;
@PostConstruct
public void connect() {
// DB 연결 로직
System.out.println("DB 연결 설정 완료");
}
@PreDestroy
public void disconnect() {
// DB 연결 해제 로직
System.out.println("DB 연결 해제");
}
} @Component
public class CacheManager {
private Map<String, String> cache = new HashMap<>();
@PostConstruct
public void loadCache() {
// 캐시 데이터 로딩
System.out.println("캐시 데이터 로딩 완료");
}
@PreDestroy
public void clearCache() {
// 캐시 데이터 삭제
cache.clear();
System.out.println("캐시 정리 완료");
}
}