포인트컷 지시자

이성준·2022년 9월 20일
0

Spring

목록 보기
8/11

포인트컷 지시자의 종류와 주의사항에 대해서 알아보도록 하겠습니다.

포인트컷 지시자

예시로 쓸 클래스

	@ClassAop
	@Component
	public class MemberServiceImpl implements MemberService {
	
    @Override
	@MethodAop("test value")
	public String hello(String param) 
    {
	return "ok";
	}
	public String internal(String param)
    {
	return "ok";
	}
}

execution

링크

within

특정 타입 내의 조인 포인트를 매칭합니다. 해당 타입이 매칭되면 그안의 조인포인트들이 자동으로 매칭
->execution에서 타입 부분만 사용한다고 생각

void withinExact() {
pointcut.setExpression("within(hello.aop.member.MemberServiceImpl)");
assertThat(pointcut.matches(helloMethod, MemberServiceImpl.class)).isTrue();

within표현식 안에 있는 모든 조인포인트들이 매칭됩니다. 하지만 표현식에 부모 타입을 지정하면 안됩니다. (execution은 가능)

void withinSuperTypeFalse() {
pointcut.setExpression("within(hello.aop.member.MemberService)");
assertThat(pointcut.matches(helloMethod,
MemberServiceImpl.class)).isFalse();
}

args

인자가 주어진 타입의 인스턴스인 조인 포인트로 매칭이 가능합니다.
문법은 execution의 args 부분이랑 같고 execution은 파라미터 타입이 정확하게 매칭 되어야하지만, args는 부모 타입을 허용합니다.



assertThat(pointcut("args(Object)")
.matches(helloMethod, MemberServiceImpl.class)).isTrue();


assertThat(pointcut("execution(* *(Object))") //매칭 실패
.matches(helloMethod, MemberServiceImpl.class)).isFalse();

execution은 메서드의 시그니쳐로 판단해서 정확하게 매칭이 되어야 하는데 , args는 런타임의 전달된 인수로 판단해서 부모타입도 허용합니다.

this,target

this는 스프링 빈 객체(스프링 aop 프록시)를 대상으로 하는 조인 포인트고
target은 target객체(스프링 aop 프록시가 가르키는 실제 대상)을 대상으로 하는 조인포인트입니다.

  • 예외 케이스 : JDK 동적 프록시를 적용했을때에는 타입을 구체클래스로 지정하면

    jdk 동적 프록시로 만든 프록시는 target을 아예 모른다.

@target, @within

@target : 실행객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
@within : 주어진 애노테이션이 있는 타입 내 조인 포인트


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAop {
} 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAop {
String value();
}

같이 타입이 있는 애노테이션으로 aop 적용 여부를 판단한다.

@Test
void success() {
log.info("child Proxy={}", child.getClass());
child.childMethod(); //부모, 자식 모두 있는 메서드
child.parentMethod(); //부모 클래스만 있는 메서드
}

static class Parent {
public void parentMethod(){} //부모에만 있는 메서드
}

@ClassAop
static class Child extends Parent {
public void childMethod(){}
}

@Slf4j
@Aspect
static class AtTargetAtWithinAspect {
//@target: 인스턴스 기준으로 모든 메서드의 조인 포인트를 선정, 부모 타입의 메서드도 적용
@Around("execution(* hello.aop..*(..)) &&
@target(hello.aop.member.annotation.ClassAop)")
public Object atTarget(ProceedingJoinPoint joinPoint) throws Throwable
{
log.info("[@target] {}", joinPoint.getSignature());
return joinPoint.proceed();
}

classAop 어노테이션이 있는 클래스들의 모든 조인포인트들에 aop가 걸립니다. 부모타입도 포함

//@within: 선택된 클래스 내부에 있는 메서드만 조인 포인트로 선정, 부모 타입의 메서드는
적용되지 않음
@Around("execution(* hello.aop..*(..)) &&
@within(hello.aop.member.annotation.ClassAop)")
public Object atWithin(ProceedingJoinPoint joinPoint) throws Throwable
{
log.info("[@within] {}", joinPoint.getSignature());

classAop 어노테이션이 있는 클래스들의 모든 조인포인트들에 aop가 걸립니다. 부모타입은 포함X

@annotation

메서드가 주어진 어노테이션을 가지고 있는 조인 포인트를 매칭합니다.

public class MemberServiceImpl {
	@MethodAop("test value")
	public String hello(String param) {
		return "ok";
	}
}
	@Around("@annotation(hello.aop.member.annotation.MethodAop)")
	public Object doAtAnnotation(ProceedingJoinPoint joinPoint) throws
Throwable {
		log.info("[@annotation] {}", joinPoint.getSignature());
		return joinPoint.proceed();
		}

MemberServiceImpl은 조인포인트에 MethodAop애노테이션을 가지고 있으니 매칭합니다.

@args

전달된 실제 인수의 런타인 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트

bean

@Around("bean(orderService) || bean(*Repository)")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[bean] {}", joinPoint.getSignature());
return joinPoint.proceed();
}

이러면 빈의 이름이 orderService이거나 Repository로 끝나는 곳에 aop가 걸립니다.

  • 주의사항
    args, @args, @target은 혼자 쓰면 안됩니다. 왜냐하면 얘네들은 실제 객체 인스턴스가 생성되고 실행될때 어드바이스 적용 여부를 알수 있는데 실행 시점에 일어나는 포인트 컷 적용 여부도 프록시가 있어야 실행 시점에 판단할수 있습니다. 즉 얘네들이 있으면 스프링은 모든 스프링 빈에 aop를 적용하기 때문에 스프링 내부 빈중 final로 지정한 빈이 있어서 오류가 발생합니다.

0개의 댓글