ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] Proxy 내부 호출 문제 해결 및 순환 참조 문제 해결
    Development/Spring 2024. 8. 28. 17:35
    728x90
    반응형

    Proxy 내부 호출

    @Component
    @Slf4j
    public class A {
        public void external() {
            log.info("external");
            internal();
        }
        public void internal() {
            log.info("internal");
        }
    }
    
    ---
    
    @Aspect
    @Slf4j
    public class AllAspect {
        @Before("execution(* *(..))")
        public void doA(JoinPoint joinPoint) {
            log.info("aop = {}", joinPoint.getSignature());
        }
    }

    위 상황에서 external()을 호출하면 doA 메서드에서 작성한 로그는 external()에서만 출력됩니다.

    이는 Spring이 Proxy 방식을 사용하여 AOP를 적용하기 때문입니다. Proxy 객체는 external() 호출을 처리한 후 실제 객체의 internal() 메서드를 호출하기 때문에 internal() 메서드에서는 AOP가 적용되지 않습니다.

     

    이를 해결하기 위해서는 external()에서 internal()을 호출할 때 this가 아닌 Proxy 객체를 사용해야 합니다.

    Proxy 객체를 사용한 해결 방법

    @Component
    public class A {
        private A a;
        
        @Autowired
        public void setB(A a) {
            this.a = a;
        }
        
        public void external() {
            log.info("external");
            a.internal();
        }
        
        public void internal() {
            log.info("internal");
        }
    }

    위 코드에서는 external()에서 internal()을 호출할 때 Proxy 객체를 사용하도록 설정했습니다.

    Setter 주입을 사용한 이유는 아직 초기화되지 않은 객체를 생성자 주입으로 처리할 수 없기 때문입니다.

    Setter 주입은 생성자 주입보다 늦게 초기화되므로 이러한 순환 참조 문제를 해결할 수 있습니다.

     

    하지만 SpringBoot 2.6 이상에서는 기본적으로 이러한 순환 참조가 금지되어 있습니다.

    만약 위와 같은 방식으로 진행하려면 application.properties에 다음 설정을 추가해야 합니다:

    spring.main.allow-circular-references=true

    설정 없이 해결하는 방법

    Spring에서는 @Lazy와 ObjectProvider를 사용하여 순환 참조 문제를 해결할 수 있습니다.

    @Lazy

    JPA를 공부하면서 Lazy는 지연 로딩에서 사용한다는 것을 듣고 해당 어노테이션을 보니 이해가 잘 되었습니다.

    @Lazy는 의존성 주입을 지연시켜 생성자 주입보다 이후에 주입이 이루어지도록 합니다.

    이로 인해 순환 참조 문제를 해결할 수 있습니다.

    @Component
    public class A {
        privat A a;
        
        @Autowired
        public A (@Lazy A a) {
            this.a = a;
        }
        
        ... method ...
    }

    ObjectProvider

    ObjectProvider는 스프링에서 제공해주는 빈을 컨테이너에 요청하면 찾아주는 기능을 제공합니다.

    @Component
    public class A {
        private final ObjectProvider<A> aProvider;
        
        @Autowired
        public A (ObjectProvider<A> aProvider) {
            this.aProvider = aProvider;
        } 
        
        public void external() {
            log.info("external");
            A a = aProvider.getObject();
            a.internal();
        }
    }

    AOP를 통해 빈 컨테이너에 저장된 객체는 Proxy이기 때문에 getObject()를 통해 찾은 객체 또한 Proxy이기 때문에 내부 객체를 호출하지만 어드바이스가 적용되는것을 확인할 수 있습니다.

    728x90
    반응형
Designed by Tistory.