티스토리 뷰

하루지식

AOP 연결된 self.invocation 문제

✨✨✨✨✨✨✨ 2024. 4. 12. 11:37
반응형
  1. 스프링 AOP와 프록시 생성: 스프링 프레임워크에서는 AOP를 구현하기 위해 주로 프록시 기반의 접근 방식을 사용합니다. @Component 등으로 선언된 빈이 AOP 어드바이스(예: @Around, @Before 등)의 대상이 될 때, 스프링은 이 빈을 위한 프록시 객체를 생성하고, 이 객체를 스프링 컨텍스트의 빈으로 등록합니다.
  2. 인터페이스 기반 프록시: 스프링은 기본적으로 인터페이스가 있는 경우 JDK 동적 프록시를 사용하여 해당 인터페이스를 구현하는 프록시 객체를 생성합니다. 인터페이스가 없는 클래스의 경우, CGLIB를 사용하여 실제 클래스의 서브 클래스를 만드는 방법으로 프록시를 생성합니다.
  3. 프록시와 Heap 영역: 프록시 객체들은 스프링의 애플리케이션 컨텍스트 내에서 관리되며, Java의 Heap 영역에 저장됩니다. 이 프록시 객체들을 통해 AOP 어드바이스가 적용된 메소드 호출이 처리됩니다.
  4. Self Invocation 문제: AOP의 주요 제약 중 하나는 'self-invocation' 즉, 같은 빈 내부에서의 메소드 호출은 AOP 프록시를 거치지 않는다는 것입니다. 이는 메소드 호출이 내부적으로 이루어지기 때문에 프록시를 통하지 않고 직접 호출되기 때문입니다. 따라서, 이러한 호출에는 AOP 어드바이스가 적용되지 않습니다.
  5. 프록시를 통한 AOP 실행: @Resource 같은 어노테이션을 사용하여 다른 빈을 주입받는 경우, 해당 빈이 AOP의 대상이면 프록시 객체가 주입됩니다. 이 프록시 객체를 통해 메소드를 호출하면 AOP 어드바이스가 적용되어 실행됩니다.

결과적으로, 스프링에서 AOP를 활용할 때는 프록시 객체를 통해 메소드를 호출해야 AOP 기능이 적용됩니다. 내부 호출에서 AOP 기능을 적용하려면, self-invocation 문제를 회피하기 위해 스프링의 AopContext.currentProxy()를 사용하거나, 다른 방법으로 간접적으로 메소드를 호출해야 합니다.

 

 

 

public interface TestService {
    void test();
    void invocTest();
}

@Service
public class TestServiceImpl implements TestService {
    @Resource TestService testService;

    @TestLog(apiType = TestEnum.MONDAY)
    @Override
    public void test() {
        System.out.println("test Start");
        testService.invocTest();
//        invocTest(); // proxy객체 아님
        System.out.println("test End");
    }

    @TestLog(apiType = TestEnum.THURSDAY)
    @Override
    public void invocTest() {
        System.out.println("invocTest");
    }
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestLog {
    TestEnum apiType();
}

@Aspect
@Component
public class LoggingAspect {

    // 메소드 실행 전에 로그를 출력
//    @Around("execution(* com.gbitkim.test.TestServiceImpl.*(..))")
    @Around("@annotation(com.gbitkim.test.TestLog)")
    public void logBeforeServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before AOP method: " + joinPoint.getSignature().getName());
        joinPoint.proceed(); // Proceed to the target method
        System.out.println("After AOP method: " + joinPoint.getSignature().getName());
    }
}

@RequiredArgsConstructor
@RestController
public class Main {
    private final TestService testService;

    @RequestMapping("/test")
    public ResponseEntity<String> call() {
        testService.test();
        return ResponseEntity.ok("Test executed successfully");
    }
}
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함