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