SSAFY11th-book-study / book-study

0 stars 0 forks source link

[7.6] configuration에서 proxy 동작 방식 #88

Open gmelon opened 1 month ago

gmelon commented 1 month ago

책에서 설명하고 있는대로 아래와 같이 config 파일을 작성하면, OtherService를 생성할 때 someService() 를 직접 호출함에도 불구하고 OtherService에 싱글톤 빈이 주입됩니다.

Config

@Configuration
public class Config {

    @Bean
    public SomeService someService() {
        return new SomeService();
    }

    @Bean
    public OtherService otherServiceA() {
        return new OtherService(someService());
    }

    @Bean
    public OtherService otherServiceB() {
        return new OtherService(someService());
    }

    static class SomeService {

    }

    static class OtherService {
        private final SomeService someService;

        public OtherService(SomeService someService) {
            this.someService = someService;
            System.out.println(someService);
        }
    }
}
image



반면 Configuration 어노테이션의 속성을 아래와 같이 변경하면

@Configuration(proxyBeanMethods = false)

코드 그대로 매번 새롭게 생성된 인스턴스가 생성자로 전달된 것을 확인할 수 있습니다.

image

이러한 현상은 스프링이 Config 클래스에 프록시를 씌워 이미 생성된 빈은 다시 생성하지 않고 기존 빈을 재사용하기 때문이라고 알고 있습니다. 스프링이 프록시를 적용하고 사용하는 보다 자세한 과정이 궁금합니다.

sootudio commented 1 month ago

답변을 찾아보긴 했는데... 완벽히 이해된 게 아니라서, 일단 내용 전달만 하겠습니다.

1. 프록시 클래스 생성

@Configuration
public class Config {

    @Bean
    public SomeService someService() {
        return new SomeService();
    }

    @Bean
    public OtherService otherServiceA() {
        return new OtherService(someService());
    }

    @Bean
    public OtherService otherServiceB() {
        return new OtherService(someService());
    }
}
public class Config$$EnhancerBySpringCGLIB extends Config {
    private SomeService someService;

    @Override
    public SomeService someService() {
        if (this.someService == null) {
            this.someService = super.someService();
        }
        return this.someService;
    }

    @Override
    public OtherService otherServiceA() {
        return new OtherService(someService());
    }

    @Override
    public OtherService otherServiceB() {
        return new OtherService(someService());
    }
}

2. 프록시 클래스의 동작

someService()메서드가 호출되면, 프록시는 내부적으로 캐싱된 SomeService 객체가 있는지 확인합니다. 만약 캐싱된 객체가 없다면, super.someService()를 호출하여 실제 SomeService 객체를 생성하고 이를 캐싱합니다. 이후 someService() 메서드가 다시 호출되면, 프록시는 캐싱된 SomeService 객체를 반환합니다.

3. 실제 호출 시

Config config = new Config$$EnhancerBySpringCGLIB();
OtherService serviceA = config.otherServiceA();
OtherService serviceB = config.otherServiceB();