Spring에서 Bean의 생명주기와 의존성 주입(DI, Dependency Injection) 방식에 대해 알아보자
스프링 빈은 스프링 컨테이너에 의해서 관리되는 자바 객체를 의미한다.
그러면 스프링 빈은 어떻게 관리되는 걸까?
스프링 컨테이너에서는 이 빈의 생성부터 소멸까지를 개발자 대신 관리해준다.
방식 | 장점 | 단점 | 사용 예시 |
---|---|---|---|
@Component | 자동 등록, 간결함 | 세부 설정 어려움 | 일반적인 Spring Boot 프로젝트 |
@Bean | 세밀한 제어 가능, 외부 객체 등록 가능 | 코드가 길어질 수 있음 | 설정 클래스로 관리할 때 |
XML 설정 | XML로 관리 가능 | 유지보수 어려움, 가독성 낮음 | 과거 프로젝트 (현재 비추천) |
컨테이너는 크게 두 종류로 나눌 수 있는다. 하나는 BeanFactory
이고, 다른 하나는 ApplicationContext
이다.
BeanFactory: 스프링 컨테이너의 가장 기본적인 형태로, 객체를 생성하고 관리하는 기능을 제공. 즉, DI 긴능만 제공하는 최소한의 컨테이너
ApplicationContext: BeanFactory를 확장한 개념으로, 부가적인 기능들이 추가된 Spring의 핵심 컨테이너
스프링의 생명주기는 단계는 다음과 같다.
1️⃣ 컨테이너 초기화: ApplicationContext가 생성되고 설정 정보를 로드
2️⃣ Bean 인스턴스 생성: @Component
, @Bean
등을 찾아 Bean 객체 생성
3️⃣ 의존성 주입: @Autowired
, 생성자 주입을 통해 의존성 연결
4️⃣ 초기화 콜백: @PostConstruct
, init-method 등을 통해 추가 설정
5️⃣ 사용 단계: 애플리케이션이 정상 동작하며 Bean을 사용
6️⃣ 소멸 콜백: @PreDestroy
, destroy-method 등을 통해 정리
AppConfig.class를 구성 정보 지정
Spring에서 의존성 주입(Dependency Injection, DI)에는 세 가지 주요 방식이 있음.
각 방식의 특징과 차이를 비교하고, 어떤 방식이 가장 좋은 선택인지 알아보자.
스프링 컨테이너에서 객체 Bean을 먼저 생성해두고 생성한 객체를 지정한 객체에 주입하는 방식을 의존성 주입이라고 한다. 객체 자체가 코드 상에서 객체 생성에 관여하지 않아도 되기때문에 객체 사이의 의존도를 낮출수 있고, 유연하고 확장성이 뛰어난 코드를 작성할 수 있다.
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 1️⃣ 데이터 저장 역할 (Repository)
@Repository
class MemberRepository {
public void save(String name) {
System.out.println(name + " 저장 완료!");
}
}
// 2️⃣ 서비스 로직 (Service) - 생성자 주입 방식 사용
@Service
class MemberService {
private final MemberRepository memberRepository;
@Autowired // Spring이 자동으로 주입
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public void registerMember(String name) {
memberRepository.save(name);
System.out.println(name + " 등록 완료!");
}
}
// 3️⃣ 스프링 애플리케이션 실행 (Application)
@SpringBootApplication
public class DependencyInjectionExample {
public static void main(String[] args) {
var context = SpringApplication.run(DependencyInjectionExample.class, args);
// 컨테이너에서 MemberService Bean 가져와 실행
MemberService memberService = context.getBean(MemberService.class);
memberService.registerMember("홍길동");
}
}
@Repository
를 사용해 MemberRepository
를 빈으로 등록 @Service
를 사용해 MemberService
를 빈으로 등록 @Autowired
로 MemberRepository
가 MemberService
에 자동 주입됨 @Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired // Spring 4.3+에서는 생략 가능
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
final
키워드를 사용하여 의존성을 반드시 설정하도록 강제할 수 있음. @Autowired
없이도 자동 주입 가능. @Service
public class MemberService {
@Autowired
private MemberRepository memberRepository;
}
final
사용 불가) @Service
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
null
가능). 결론: "생성자 주입"을 사용하자!
✅ 불변성 보장 (final
사용 가능)
✅ 테스트 용이 (Spring 없이도 순수한 자바 코드로 테스트 가능)
✅ Spring 4.3+에서는 @Autowired
없이 자동 주입 가능
✅ 의존성이 명확하게 드러남 → 유지보수 용이