본문 바로가기
java

Self Invocation 이슈와 해결 방법

by jasNote 2024. 4. 8.

Self Invocation 이란

  • 한 클래스내의 내부 메서드가 또다른 내부 메서드를 호출하는 것

Self Invocation 이슈

  • 호출하는 내부 메서드에 @Cacheable, @Transactional 와 같은 어노테이션이 설정되어 있다면 해당 기능들이 미작동할 수 있다.

예시

  • getOnegetTwo 를 Self Invocation(내부 호출)하고 있다.
  • getTwo@Cacheable 함수이다. 그러므로 첫 호출에는 로직을 타지만, 두번째 호출에는 로직을 타지 않고 캐싱된 값을 반환해야 한다.
  • run실행 결과를 예상해보자.
    • 예상. Read Two 로그가 한번 찍힌다.
    • 실제. Read Two 로그가 두번 찍힌다.
@Component
public class CacheTest{

    public String getOne(){
        getTwo();
        return "One"
    }

    @Cacheable(value = "two")
    public String getTwo() {
        log.info("Read Two")
        return "two"
    }
}

@Component
@RequiredArgsConstructor
public class Main {
    private final CacheTest cacheTest;
    
    public void run(){
        cacheTest.getOne():
        cacheTest.getOne():
    }
}

이슈 원인

내부(this)에서 직접 호출했기 때문이다. @Cacheable, @Transactional, @Async 등. 이 익숙한 어노테이션들은 프록시를 통해 동작한다.

 

Spring 환경에서는 보통 Bean을 주입받아 해당 객체의 메서드를 실행하는데, 이때 내부 동작 원리를 보면 메소드를 직접적으로 호출하는 것이 아니다. Spring AOP 기반으로 동작한다. 호출을 가로채어 Proxy객체가 부가기능들(advisor)을 수행하고 대신 메서드를 호출하게 된다.

해결 방법

방법1. AppContext 활용

  • 5번 line의 AopContext의 proxy함수를 활용하여 호출하는 방법
@Component
public class CacheTest{

    public String getOne(){
        ((CacheTest)AopContext.currentProxy()).getTwo(); # 핵심
        return "One"
    }

    @Cacheable(value = "two")
    public String getTwo() {
        log.info("Read Two")
        return "two"
    }
}

 

방법2. 자기참조

  • 5,8 line 본인을 주입받아 호출한다.
@Component
@RequiredArgsConstructor
public class CacheTest{
  
    private final CacheTest cacheTest

    public String getOne(){
        cacheTest.getTwo();
        return "One"
    }

    @Cacheable(value = "two")
    public String getTwo() {
        log.info("Read Two")
        return "two"
    }
}

 

방법3. 외부 분리

  • 클래스를 분리한다.
  • 여러 방법이 있지만 구조적으로 분리하여 의미있는 설계가 된다면 가장 클린한 방법이라고 생각한다.
@Component
@RequiredArgsConstructor
public class CacheTest{
  
    private final CacheTest cacheTest2

    public String getOne(){
        cacheTest2.getTwo();
        return "One"
    }
}

@Component
public class CacheTest2{
    @Cacheable(value = "two")
    public String getTwo() {
        log.info("Read Two")
        return "two"
    }
}

'java' 카테고리의 다른 글

독립성과 은닉성(클래스와 모듈)  (0) 2024.11.03