포인트컷 지시자
@annotation
@annotation : 메서드가 주어진 어노테이션을 가지고 있는 조인 포인트를 매칭한다.
@Component
public class MemberServiceImpl implements MemberService{
@Override
@MethodAop("test value")
public String hello(String param) {
return "ok";
}
}@Slf4j
@SpringBootTest
@Import(AtAnnotationTest.AtAnnotationAspect.class)
public class AtAnnotationTest {
@Autowired MemberService memberService;
@Test
void success() {
log.info("memberService Proxy={}", memberService.getClass());
memberService.hello("helloA");
}
@Slf4j
@Aspect
static class AtAnnotationAspect{
@Around("@annotation(hello.aop.member.annotation.MethodAop)")
public Object doAtAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[@annotation] {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
}bean
스프링 전용 포인트컷 지시자로 빈의 이름으로 지정한다.
스프링 빈의 이름으로 AOP 적용 여부를 지정할 수 있다. 스프링에서만 사용할 수 있는 특별한 지시자이다.
*과 같은 패턴을 사용할 수 있다.
매개변수 전달
다음은 포인트컷 표현식을 사용해서 어드바이스에 매개변수를 전달할 수 있다.
this,target,args,@target,@within,@annotation,@args
logArgs1:joinPoint.getArgs()[0]과 같이 매개변수를 전달 받는다.logArgs2:args(arg, ..)와 같이 매개변수를 전달 받는다.logArgs3:@Before를 사용한 축약 버전이다. 타입을String으로 제한했다.this: 프록시 객체를 전달 받는다.target: 실제 대상 객체를 전달 받는다.@target,@within: 타입의 어노테이션을 전달 받는다.@annotation: 메서드의 어노테이션을 전달 받는다. 해당 어노테이션의 값을 받아서 사용할 수도 있다.
this와 target
this: 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트target: Target 빈 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트
this와 target은 적용 타입 하나를 정확하게 지정해야 한다. 여기서 *같은 패턴을 사용할 수 없으며 부모 타입을 허용한다.
단순히 타입 하나를 정하는 것인데 둘은 어떤 차이가 있을까?
스프링에서 AOP를 적용하면 실제 target 객체 대신에 프록시 객체가 스프링 빈으로 등록된다.
this는 스프링 빈으로 등록되어 있는 프록시 객체를 대상으로 포인트컷을 매칭한다.target은 실제 target 객체를 대상으로 포인트컷을 매칭한다.
스프링은 프록시를 생성할 때 JDK 동적 프록시와 CGLIB를 선택할 수 있는데 둘의 프록시를 생성하는 방식이 다르기 때문에 차이가 발생한다.
JDK 동적 프록시 : 인터페이스를 구현한 프록시 객체를 생성한다.(인터페이스가 필수)
CGLIB : 인터페이스가 있어도 구체 클래스를 상속 받아서 프록시 객체를 생성한다.
JDK 동적 프록시를 적용했을 때

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

포인트컷으로 인터페이스 지정
this(...MemberService): proxy 객체를 보고 판단한다.this는 부모 타입을 허용하기 때문에 AOP가 적용된다.target(...MemberService): target 객체를 보고 판단한다.target은 부모 타입을 허용하기 때문에 AOP가 적용된다.
포인트컷으로 구체 클래스 지정
this(...MemberServiceImpl): proxy 객체를 보고 판단한다. CGLIB 프록시로 만들어진 프록시 객체는 구체 클래스를 상속받아서 만들었기 때문에 AOP가 적용된다.this가 부모 타입을 허용하기 때문에 포인트컷의 대상이 된다.
target(...MemberServiceImpl): target 객체를 보고 판단한다. target 객체가 구체 클래스 타입이므로 AOP 적용 대상이다.
프록시를 대상으로 하는 this의 경우 구체 클래스를 지정하면 프록시 생성 전략에 따라서 다른 결과가 나올 수 있다.
application.properties에 적용하는 대신에 위 방식으로 하면 각 테스트마다 다른 설정을 손쉽게 적용할 수 있다.
JDK 동적 프록시를 사용하면
this-impl부분이 출력되지 않는다.this는 스프링 AOP 프록시 객체를 대상으로 하는데 JDK 동적 프록시는 인터페이스를 기반으로 생성되므로 구체 클래스를 알 수 없다.
참고 :
this,target지시자는 단독으로 사용되기 보다는 파라미터 바인딩에서 주로 사용된다.
Last updated