@Configuration과 proxyBeanMethods

Sol's·2023년 5월 30일
0

토비의 스프링 부트

목록 보기
20/31

학습테스트를 해보겠습니다.
남이 만들 코드의 동작방식을 이해하기 위해 테스트를 작성하는것을 말합니다.
기술을 정확하게 익히기 위해 사용합니다

@Configuration 테스트

Configuration클래스는 Bean메소드를 많이 가지고 있다는 것입니다.
팩토리 메서드의 기능을 생각하면 됩니다.
하지만 이것이 생각보다 간단하지 않습니다.
팩토리 메서드는 싱글톤을 보장해주지 못하기 때문입니다.

스프링 Bean싱글톤을 보장해 줍니다.
하지만 자바 코드로 팩토리 메서드를 만들면, 싱글톤이 보장되지 않습니다.

public class ConfigurationTest {
    @Test
    void configuration() {
        MyConfig myConfig = new MyConfig();
        Bean1 bean1 = myConfig.bean1();
        Bean2 bean2 = myConfig.bean2();
        Assertions.assertThat(bean1).isSameAs(bean2);
    }

    @Configuration
    static class MyConfig{
        @Bean
        Common common() {
            return new Common();
        }
        @Bean
        Bean1 bean1() {
            return new Bean1(common());
        }
        @Bean
        Bean2 bean2() {
            return new Bean2(common());
        }
    }
    static class Bean1 {
        private final Common common;

        public Bean1(Common common) {
            this.common = common;
        }
    }

    static class Bean2 {
        private final Common common;

        public Bean2(Common common) {
            this.common = common;
        }
    }

    static class Common {

    }
}

스프링의 도움이 없이 팩토리 메서드의 흉내를 낸다면 싱글톤을 보장하지 않습니다.

스프링 컨네이너 사용

스프링 컨테이너를 사용하면 자바코드와는 달리 싱글톤을 보장합니다.
내부적으로는 프록시 오브제트가 하나 등록이 되고 그것이 Bean으로 등록됩니다.

public class ConfigurationTest {
    @Test
    void configuration() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.register(MyConfig.class);
        ac.refresh();
        Bean1 bean1 = ac.getBean(Bean1.class);
        Bean2 bean2 = ac.getBean(Bean2.class);
        Assertions.assertThat(bean1.common).isSameAs(bean2.common);
    }

    @Configuration
    static class MyConfig{
        @Bean
        Common common() {
            return new Common();
        }
        @Bean
        Bean1 bean1() {
            return new Bean1(common());
        }
        @Bean
        Bean2 bean2() {
            return new Bean2(common());
        }
    }
    static class Bean1 {
        private final Common common;

        public Bean1(Common common) {
            this.common = common;
        }
    }

    static class Bean2 {
        private final Common common;

        public Bean2(Common common) {
            this.common = common;
        }
    }

    static class Common {

    }
}

스프링이 어떻게 싱글톤을 보장할 수 있을까요??
바로 프록시 패턴의 역할을 하는 @Configuration의 proxyBeanMethods 때문입니다.

proxyBeanMethods

클래스를 확장하는 방식으로 만들어, 클래스 앞에서 객체를 필드값으로 저장하여 반환하기때문에 싱글톤을 흉내낼 수 있습니다.
스프링은 내부적으로 proxyBeanMethods를 True로 설정하고 있습니다.

 @Test
    void proxyCommonMethod() {
        MyConfigProxy myConfigProxy = new MyConfigProxy();
        Bean1 bean1 = myConfigProxy.bean1();
        Bean2 bean2 = myConfigProxy.bean2();

        Assertions.assertThat(bean1.common).isSameAs(bean2.common);
    }

    static class MyConfigProxy extends MyConfig {
        private Common common;
        @Override
        Common common() {
            if (this.common == null) this.common = super.common();

            return this.common;
        }
    }

프록시 오브젝트가 Common메서드가 생성하는 오브젝트의 개수를 제한함으로써 싱글톤을 흉내내었습니다.

Configuration의 proxyBeanMethods 기본값

Configuration가 기본 proxyBeanMethods=True인것은
스프링 컨테이너가 싱글톤으로 객체를 띄우기 위해 프록시 패턴을 사용한다는 뜻입니다.

proxyBeanMethods=False라면, Bean들은 더이상 싱글톤이 아닙니다!

배운점

자바로 팩토리 메서드를 구현할때와,
스프링 컨테이너로 등록한 팩토리 메서드는 다르게 작동한다는 것입니다!!

내부적으로 프록시 패턴이 적용되기 떄문입니다!

profile
배우고, 생각하고, 행동해라

0개의 댓글