@Bean과 static method
by youngjun._.인프런 강의(스프링 핵심 원리 기본) 내용 중 Spring Container는 Bean을 싱글톤으로 관리하고 이를 확인하는 테스트 코드를 실행하다가 @Configuration class에서 Bean을 등록하는 method가 static인 경우 싱글톤이 보장되지 않아 이유가 궁금해서 찾아보았다.
기존 코드
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository());
}
}
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
// 값 3개가 같다
System.out.println("memberService -> memberRepository1 = " + memberRepository1);
System.out.println("orderService -> memberRepository2 = " + memberRepository2);
System.out.println("memberRepository -> memberRepository3 = " + memberRepository);
assertSame(memberRepository1, memberRepository2);
assertSame(memberRepository1, memberRepository);
}
}
수정된 코드
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public static MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository());
}
}
memberRepository() method를 static으로 변경했을 뿐인데 싱글톤이 보장되지 않아 테스트는 실패한다.
원인 분석
@Bean 애너테이션에 어떤 로직이 숨어있는지 먼저 파악해보기로 했다.
1. static 키워드 때문에 BFPP로 인식하는 것 같다
위 @Bean 애너테이션을 보면 다음과 같이 정리할 수 있다.
1. BeanFactoryPostProcessor(BFPP) 객체는 컨테이너 생명주기 초기에 인스턴스화 해야한다.
2. BFPP 객체를 초기에 인스턴스화 하면 @Autowired, @Value, @PostConsruct 같은 애너테이션의 처리를 방해할 수 있다.
3. 이를 예방하기 위해 BFPP를 리턴하는 @Bean 메서드를 static으로 선언하면 된다.
2. BFPP는 뭘까
BeanFactoryPostProcessor는 해석 그대로 Bean Factory 후처리기이다. (javaDoc link)
Spring Bean이 초기화 되는 과정은 아래 그림이 가장 이해하기 쉬워서 가져왔다.
가장 처음 Load Bean Definitions 의 Post Process 부분이 BFPP이고 Bean의 정의 자체를 바꿀 수 있다.
설정 메타데이터를 읽고 인스턴스들의 실행 순서 등을 제어할 수 있다.
BFPP 구현 예제는 Spring Core article에서 확인해보자.
동작 원리와 static 으로 선언해야 하는 이유에 대한 자세한 내용은 다른 블로그 글을 참고하면 좋을 것 같다.
정리하면, Bean을 등록하는 메서드에 static 키워드가 붙으면 Spring 컨테이너의 life sycle 매우 초기에 다른 Bean들보다 먼저 등록된다. 따라서 @Configuration 클래스들이 생성되기 전에 호출이 가능하다.
추가로 refreshContext 동작 과정도 공부해보자.
'Back-End > Spring' 카테고리의 다른 글
Spring boot Swagger 3.0 적용하기 (0) | 2022.07.30 |
---|
블로그의 정보
개발하는만두
youngjun._.