맨땅에 헤딩하는 개바른자

[AOP] this, target 본문

JAVA/AOP

[AOP] this, target

앵낄낄 2023. 3. 29. 21:10
반응형

[AOP] this, target는 스프링 프레임워크에서 사용되는 개념입니다.

  • this: 메소드 실행 객체**(프록시객체)**를 참조하는 키워드입니다.
  • target: 실제 메소드가 구현된 객체를 참조하는 키워드입니다.

이를 이용하여, 스프링에서 AOP를 구현할 때, 메소드 실행 전/후에 추가적인 작업을 수행할 수 있습니다.

예를 들어, 로깅 기능을 추가하고 싶다면, AOP를 활용하여 해당 메소드 실행 전/후에 로깅을 수행할 수 있습니다.

이처럼, [AOP] this, target는 스프링에서 AOP를 구현하는 데 필수적인 개념이므로, 이를 잘 이해하고 활용하는 것이 중요합니다.

this와 target은 다음과 같이 적용타입 하나를 정확하게 지정해야 한다.

this(hello.aop.member.MemberService)
target(hello.aop.member.MemberService)

 

  • 별표와 같은 (*) 표현식은 사용할 수 없다.
  • 부모 타입을 허용한다.
  • 실제 사용성은 낮은 편인다.

 

프록시 생성 방식에 따른 차이

  • JDK 동적 프록시 : 인터페이스가 필수이고, 인터페이스를 구현한 프록시 객체를 생성한다.
  • CGLIB : 인터페이스가 있어도 구체 클래스를 상속 받아서 프록시 객체를 생성한다.

 

먼저 JDK 동적프록시 적용을 알아보자

  • this(MemberService) O
    • proxy 객체를 보고 판단한다. this는 부모타입을 허용하기 때문에 AOP가 적용된다.
  • targer(MemberService) O
    • target 객체를 보고 판단한다. this는 부모타입을 허용하기 때문에 AOP가 적용된다.
  • this(MemberServiceImpl) X
    • proxy 객체를 보고 판단한다. JDK 동적 프록시로 만들어진 proxy 객체를 MemberService 인터페이스를 기반으로 구현된 새로운 클래스다. 따라서 MemberServiceImpl를 전혀 알지 못하므로 AOP 적용대상이 아니다.
  • targer(MemberServiceImpl)
    • target 객체를 보고 판단한다. target객체가 MemberServiceImpl타입이므로 AOP 적용 대상이다.

 

CGLIB 프록시 적용을 알아보자

  • this(MemberService) O
    • proxy 객체를 보고 판단한다. this는 부모타입을 허용하기 때문에 AOP가 적용된다.
  • targer(MemberService) O
    • target 객체를 보고 판단한다. this는 부모타입을 허용하기 때문에 AOP가 적용된다.
  • this(MemberServiceImpl) X
    • proxy 객체를 보고 판단한다. CGLIB로 만들어진 만들어진 proxy 객체를 MemberServiceImpl를 상속받아서 만들었기 때문에 AOP가 적용된다. this가 부모 타입을 허용하기 때문에 포인트컷의 대상이 된다.
  • targer(MemberServiceImpl)
    • target 객체를 보고 판단한다. target객체가 MemberServiceImpl타입이므로 AOP 적용 대상이다.

 

예제코드

https://github.com/ymwoo88/study-springboot-aop/tree/feature/스프링-AOP-this-target

package hello.aop.pointcut;

import hello.aop.member.MemberService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

/**
 * application.properties
 * spring.aop.proxy-target-class=true CGLIB
 * spring.aop.proxy-target-class=false JDK 동적 프록시
 */
@Slf4j
@Import(ThisTargetTest.ThisTargetAspect.class)
@SpringBootTest(properties = "spring.aop.proxy-target-class=true")
//@SpringBootTest(properties = "spring.aop.proxy-target-class=false")
public class ThisTargetTest {

    @Autowired
    MemberService memberService;

    @Test
    void success() {
        log.info("memberService Proxy={}", memberService.getClass());
        memberService.hello("helloA");
    }

    // 참고로 스프링트에서 프로식 사용 시 무조건 CGLIB로 동작하게되어있어서 JDK동적프록시로 돌아가게끔 별도 설정이 필요하다
    @Slf4j
    @Aspect
    static class ThisTargetAspect {
        // 부모타입을 허용
        @Around("this(hello.aop.member.MemberService)")
        public Object doThisInterface(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[this-interface] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
        // 부모타입
        @Around("target(hello.aop.member.MemberService)")
        public Object doTargetInterface(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[target-interface] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
        // 구현체타입
        @Around("this(hello.aop.member.MemberServiceImpl)")
        public Object doThisImpl(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[this-impl] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
        // 구현체타입
        @Around("target(hello.aop.member.MemberServiceImpl)")
        public Object doTargetImpl(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[target-impl] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
    }
}

결과

  • JDK 동적 프록시
2023-03-29 21:06:04.718 INFO 65949 --- [ Test worker] hello.aop.pointcut.ThisTargetTest : memberService Proxy=class com.sun.proxy.$Proxy53 
2023-03-29 21:06:04.722 INFO 65949 --- [ Test worker] h.a.p.ThisTargetTest$ThisTargetAspect : [target-impl] String hello.aop.member.MemberService.hello(String) 
2023-03-29 21:06:04.722 INFO 65949 --- [ Test worker] h.a.p.ThisTargetTest$ThisTargetAspect : [target-interface] String hello.aop.member.MemberService.hello(String) 
2023-03-29 21:06:04.722 INFO 65949 --- [ Test worker] h.a.p.ThisTargetTest$ThisTargetAspect : [this-interface] String hello.aop.member.MemberService.hello(String)

 

  • CGLIB 프록시
2023-03-29 21:09:59.480  INFO 69151 --- [    Test worker] hello.aop.pointcut.ThisTargetTest        : memberService Proxy=class hello.aop.member.MemberServiceImpl$$EnhancerBySpringCGLIB$$dc929420
2023-03-29 21:09:59.485  INFO 69151 --- [    Test worker] h.a.p.ThisTargetTest$ThisTargetAspect    : [target-impl] String hello.aop.member.MemberServiceImpl.hello(String)
2023-03-29 21:09:59.485  INFO 69151 --- [    Test worker] h.a.p.ThisTargetTest$ThisTargetAspect    : [target-interface] String hello.aop.member.MemberServiceImpl.hello(String)
2023-03-29 21:09:59.486  INFO 69151 --- [    Test worker] h.a.p.ThisTargetTest$ThisTargetAspect    : [this-impl] String hello.aop.member.MemberServiceImpl.hello(String)
2023-03-29 21:09:59.486  INFO 69151 --- [    Test worker] h.a.p.ThisTargetTest$ThisTargetAspect    : [this-interface] String hello.aop.member.MemberServiceImpl.hello(String)

 

정리

프록시를 대상으로 하는 this의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수 있다.

반응형

'JAVA > AOP' 카테고리의 다른 글

[AOP] 실전예제 Service단 파라미터 Null체크 사용기  (1) 2023.04.25
[AOP] 실전예제 로그TRACE  (0) 2023.04.18
[AOP] 매개변수 전달  (0) 2023.03.29
[AOP] @annotation  (0) 2023.03.23
[AOP] @target @within  (0) 2023.03.23