로컬에서는 잘 되는데 ☘️

@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 애너테이션에 어떤 로직이 숨어있는지 먼저 파악해보기로 했다.

Bean 애너테이션 javaDoc의 static 관련 내용

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이 초기화 되는 과정은 아래 그림이 가장 이해하기 쉬워서 가져왔다.

출처 : https://www.dineshonjava.com/bean-lifecycle-and-callbacks/

가장 처음 Load Bean Definitions 의 Post Process 부분이 BFPP이고 Bean의 정의 자체를 바꿀 수 있다.

설정 메타데이터를 읽고 인스턴스들의 실행 순서 등을 제어할 수 있다.

 

BFPP 구현 예제는 Spring Core article에서 확인해보자.

동작 원리와 static 으로 선언해야 하는 이유에 대한 자세한 내용은 다른 블로그 글을 참고하면 좋을 것 같다.

 

 

 

정리하면, Bean을 등록하는 메서드에 static 키워드가 붙으면 Spring 컨테이너의 life sycle 매우 초기에 다른 Bean들보다 먼저 등록된다. 따라서 @Configuration 클래스들이 생성되기 전에 호출이 가능하다.

 

추가로 refreshContext 동작 과정도 공부해보자.

- https://mangkyu.tistory.com/214

'Back-End > Spring' 카테고리의 다른 글

Spring boot Swagger 3.0 적용하기  (0) 2022.07.30

블로그의 정보

개발하는만두

youngjun._.

활동하기