Spring Bean은 스프링 컨테이너에서 관리하는 자바 객체를 의미한다. 빈은 스프링 컨테이너에 의해 인스턴스화 되며, 인스턴스화된 빈은 getBean()
메소드를 통해 가져와서 사용할 수 있고, @Autowired
어노테이션으로 의존 객체를 주입 받을 수 있다.
어떤 객체가 사용하는 의존 객체를 직접 생성하는 것이 아니라
스프링 컨테이너에서 주입을 받아 사용하는 것이다. 즉 스프링 컨테이너에서 객체 정보를 가져오는 것이라 이해하자.
A 객체가 B 객체를 이용 (A는 B에 의존한다 = B는 A에 의존성이다.)
이제 ApplicationContext
(스프링 컨테이너)를 통해 빈들을 설정하고 어디에 의존성을 주입할 건지를 실습해보자. 빈을 설정하는 방식은 2가지가 있었다.
1. XML 설정 파일 통해 빈 정의
<!-- applicationContext.xml -->
<!-- xml 방식으로 빈 설정하기 -->
<bean id = "demoService"
class ="">
<property name="demoRepository" ref="demoRepository"/>
</bean>
...
...
<bean id = "demoRepository"
class ="">
</bean>
이후 main class에서 ApplicationContext를 활용해서 Bean을 가져와 사용하는 것이다. (DI)
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
String [] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
//Bean 단일적으로 가져오기
DemoService demoService = (DemoService) context.getBean("demoService");
}
이 방식의 단점은 모든 빈들을 일일히 등록해야 한다는 번거로움이 있다. 이번엔 component-scan
을 살펴보자.
<context:component-scan base-package="[패지키명]"/>
<!-- [ ] 패키지부터 스캔을 하여 빈으로 등록하겠다라는 의미 -->
컨테이너 생성 시점에 component-scan을 통해 전체를 스캔하여 빈으로 등록해주기 때문에 번거로움이 줄어든다.
2. 자바 설정 파일 통해 빈 정의
스캐닝을 할 때는 기본적으로
@Component
어노테이션을 사용해서 빈 등록을 한다.
@Inject는 또 다른 의존성을 필요로 한다.
그래서 주로 @Autowired를 사용한다.
@Configuration
public class ApplicatoinConfig {
@Bean
public DemoRepository demoRepository() {
return new demoRepository();
}
//setter를 활용한 의존성 주입
@Bean
public DemoService demoService() {
DemoService demoService = new DemoService();
//custService에 setter가 있으므로 직접 의존성 주입을 받을 수 있다.
demoService.setDemoRepository(demoRepository());
return DemoService;
}
//메서드 파라미터를 활용한 의존성 주입
@Bean
public DemoService demoService(DemoRepository demoRepository) {
DemoService demoService = new DemoService();
demoService.setDemoRepository(demoRepository);
return demoService;
}
//의존성 주입을 직접하지 않고 @Autowired를 사용해서 의존성 주입을 한다.
@Bean
public DemoService demoService() {
return new DemoService();
//DemoService 내에서
//@Autowired
//private DemoRepository demorepository; 및 setter
//선언 되어 있어야 함.
//즉, @Bean을 통해 Bean으로 등록되어 있어 꺼내와서 사용 가능
}
}
//ApplicationContext 활용
public static void main(String[] args) {
//해당 클래스(ApplicationConfig)를 빈 설정 파일로 사용한다.
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicatoinConfig.class);
String [] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
//Bean 단일적으로 가져오기
DemoService demoService = (DemoService) context.getBean("demoService");
}
@Component
는 코드 수정이 가능한 클래스를 객체로 생성하여 초기화해 빈으로 등록하고자 할 때 @Component를 사용한다. 직관적으로 구조 파악이 가능하다.
@Bean
은 @Configuration을 통해 자바 설정 파일임을 명시하는 클래스 안에 작성한다. (=applicationconfig)
보통 내부적으로 코드 수정이 불가능한 라이브러리를 초기화해 빈으로 등록하고자 할 때 @Bean을 사용하는 것이다.
위 ApplicationConfig를 이용한 예제 역시 빈들을 일일히 설정, 등록해야 한다. 이번엔 componentScan
을 사용해보자.
@Configuration
@ComponentScan(basePackageClasses = DemoApplication.class)
//DemoApplication은 main 객체
//해당 ComponentScan은 type safe한 방식으로 현 스프링 부트기반 가장 가까운 방법
public class ApplicationConfig {
}
XML 설정 방식에서 component-scan
을 사용한 것처럼 자바 설정에서는 componentScan
을 사용하면 전체를 스캔하여 빈 등록을 해주기 때문에 훨씬 편하다.
Spring을 비롯해 많은 Spring Container는 자신이 관리하는 Bean
의 Life-cycle을 관리해주고 특정 시점에 Bean
에게 이를 알려줄 수 있는 메커니즘을 제공한다.
기본적으로 스프링 빈은 객체 생성->의존관계 주입 과 같은 라이프 사이클을 가진다.
객체를 생성하고 의존관계 주입이 끝난 다음에야 데이터를 사용할 수 있는 준비가 완료되고 그에 따라서 초기화 작업은 url이 있는 상태, 즉 의존관계 주입이 끝난 다음에 호출해야 한다.
그러면 여기서 우리가 의존관계 주입이 끝나는 시점을 어떻게 알 수 있을까?
이 때 스프링 빈의 생명주기를 알고 콜백을 알면 유용하게 활용 가능하다.
스프링 빈의 라이프 사이클은 다음과 같이 진행된다.
스프링 컨테이너 생성 -> 스프링 빈 생성(Constructor Injection) -> 의존관계 주입(Setter Injection 및 Field Injection이 일어남) -> 초기화 콜백 사용 -> 소멸전 콜백 ->스프링 종료
여기서 중요한 부분은 콜백 부분이라고 할 수 있다.
초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
소멸전 콜백: 빈이 소멸되기 직전에 호출
그렇다면 이 콜백은 구체적으로 어떻게 사용하면 좋을까?
스프링에서 빈 생명주기 콜백에 대한 지원은 세 가지가 제공된다. 1. 어노테이션 지정
, 2. 인터페이스 구현
, 3. Bean설정 시 init, destroy 메서드 지정
이 있다. 상황에 따라 알맞는 방법을 사용하면 된다.
다음 학습에서 세 가지 방법의 활용도와 빈 스코프를 알아보자.