Question
- execution의 모든 인자를 설명하시오
- 다음을 설명하시오
- within
- args
- this
- target
- @target
- @within
- @annotation
- @args
- bean
포인트컷 지시자의 종류
- execution : 메소드 실행 조인 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하고, 기능도 복잡하
다.
- within : 특정 타입 내의 조인 포인트를 매칭한다.
- args : 인자가 주어진 타입의 인스턴스인 조인 포인트
- this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
- target : Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트
- @target : 실행 객체의 클래스에 주어진 타입의 애노테이션이 있는 조인 포인트
- @within : 주어진 애노테이션이 있는 타입 내 조인 포인트
- @annotation : 메서드가 주어진 애노테이션을 가지고 있는 조인 포인트를 매칭
- @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 애노테이션을 갖는 조인 포인트
- bean : 스프링 전용 포인트컷 지시자, 빈의 이름으로 포인트컷을 지정한다
execution 문법
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
execution(접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?)
ex) execution(public String hello.aop.member.MemberServiceImpl.hello(String))
- ?는 생략할 수 있음
- * 같은 패턴을 지정할 수 있음
- 예시 분석
- 접근제어자?: public
- 반환타입: String
- 선언타입?: hello.aop.member.MemberServiceImpl
- 메서드이름: hello
- 파라미터: (String)
- 예외?: 생략
가장 생략 많이한 포인트컷
execution(* *(..))
- 접근제어자?: 생략
- 반환타입: *
- 선언타입?: 생략
- 메서드이름: *
- 파라미터: (..)
- 예외?: 없음
- *는 아무 값이 들어와도 된다는 뜻
- ..는 파라미터 타입과 수가 상관 없다는 뜻(0..*)
패키지 맵핑 관련 포인트컷
- hello.aop.member.*(1).*(2)
- (1): 타입
- (2): 메서드 이름
- 파라미터가 나오기전 . 뒤 맨 마지막은 메서드 이름임
- 그 전에는 패키지 경로
- . 는 정확하게 해당 위치의 패키지를 의미
- ..는 해당 위치의 패키지와 그 하위 패키지도 포함한다는 의미
- 패키지는 부모 타입으로 선언하면 자식 타입도 매칭됨
- 만약 부모 타입으로 선언했느네 부모에 없는 함수를 선언하면 맵핑되지 않음
파라미터 매칭
//String 타입의 파라미터 허용
execution(* *(String))
//파라미터가 없어야 함
execution(* *())
//정확히 하나의 파라미터 허용, 모든 타입 허용
execution(* *(*))
//숫자와 무관하게 모든 파라미터, 모든 타입 허용
//파라미터가 없는 것도 가능
execution(* *(..))
//String 타입으로 시작, 숫자와 무관하게 모든 파라미터, 모든 타입 허용
//(String), (String, Xxx), (String, Xxx, Xxx) 허용
execution(* *(String, ..))
within
within(hello.aop.member.MemberServiceImpl)
within(hello.aop.member.*Service*)
within(hello.aop..*)
- 타입(클래스나 인터페이스)이 매칭되면 그 안의 메서드(조인 포인트)들이 자동으로 매칭 됨
- execution에서 타입 부분만 사용한다고 보면 됨
- execution과 다르게 표현식에 부모 타입 지정 불가하며, 정확하게 타입이 맞아야 함
args
args(String)
args()
args(..)
args(*)
args(String,..)
args(java.io.Serializable)
- 타입만 보고 매칭
- execution의 타입 매칭과 다른 점은 args는 부모 타입도 허용
- 예를 들어 String은 Serializable을 구현하므로 args에서는 String을 받아도 가능하지만 execution에서는 안됨
@within
@Aspect
@Component
public class MyAspect {
// @MyAnnotation 애노테이션이 붙은 클래스 내부의 모든 메서드에 적용
@Pointcut("@within(com.example.annotation.MyAnnotation)")
public void annotatedWithMyAnnotation() {}
@Before("annotatedWithMyAnnotation()")
public void logBefore() {
System.out.println("Method in a class annotated with @MyAnnotation is about to execute.");
}
}
// 이 클래스의 모든 메서드는 AOP가 적용됨
@MyAnnotation
public class MyService {
public void doSomething() {
// 코드
}
}
- @within은 특정 애노테이션이 붙어 있는 타입(클래스)의 내부 메서드들에 대해 포인트컷을 적용하는 데 사용함.
- 쉽게 말해서, 어떤 클래스가 특정 애노테이션을 가지고 있으면 그 클래스의 메서드들에 AOP를 적용할 수 있음.
- 컴파일 타임에 적용, 특정 애노테이션이 붙은 클래스 내부의 모든 메서드에 대해서 AOP 적용
@target
@Aspect
@Component
public class MyAspect {
// @MyAnnotation 애노테이션이 붙은 프록시 객체의 모든 메서드에 적용
@Pointcut("@target(com.example.annotation.MyAnnotation)")
public void proxyAnnotatedWithMyAnnotation() {}
@Before("proxyAnnotatedWithMyAnnotation()")
public void logBefore() {
System.out.println("Method in a proxy object annotated with @MyAnnotation is about to execute.");
}
}
// 이 클래스의 모든 메서드는 런타임 시점에 프록시 객체가 AOP 적용
@MyAnnotation
public class MyServiceImpl implements MyService {
public void doSomething() {
// 코드
}
}
- @target은 프록시 객체가 특정 애노테이션을 가지고 있는지에 따라 포인트컷을 적용하는 데 사용함.
- 즉, 해당 애노테이션이 런타임 시점에 프록시 객체 타입에 적용된 경우에만 메서드 호출에 AOP를 적용함.
- 런타임 타임에 적용, 특정 애노테이션이 붙은 프록시 객체의 모든 메서드에 대해서 AOP 적용
@annotation
public class MemberServiceImpl {
@MethodAop("test value")
public String hello(String param) {
return "ok";
}
}
@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();
}
}
- 어노테이션을 가지고 있는 메서드에 조인포인트를 매칭
@args
@Service
public class MyService {
public void performAction(@Auditable String data) {
System.out.println("Performing action with data: " + data);
}
}
@Aspect
@Component
public class MyAspect {
// @Auditable 애노테이션이 붙은 인자를 가진 메서드에 적용되는 포인트컷 정의
@Before("@args(com.example.Auditable)")
public void logBeforeAnnotatedArgs() {
System.out.println("A method with an @Auditable argument is about to execute.");
}
}
- @args는 메서드 호출 시, 인자들이 특정 애노테이션을 가지고 있는 경우에 매칭되는 포인트컷을 정의할 때 사용
- 예를 들어, 메서드 인자 중 하나 또는 여러 개가 특정 애노테이션으로 표시된 경우에만 AOP를 적용가능
bean
@Aspect
static class BeanAspect {
@Around("bean(orderService) || bean(*Repository)")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[bean] {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
- 스프링 전용으로, 빈의 이름으로 조인포인트 매칭
매개변수 전달
//joinPoint.getArgs()[0] 와 같이 매개변수를 전달 받는다
@Around("allMember()")
// args(arg,..) 와 같이 매개변수를 전달 받는다.
@Around("allMember() && args(arg,..)")
//@Before 를 사용한 축약 버전이다. 추가로 타입을 String 으로 제한했다
@Before("allMember() && args(arg,..)")
//프록시 객체를 전달 받는다.
@Before("allMember() && this(obj)")
//실제 대상 객체를 전달 받는다.
@Before("allMember() && target(obj)")
//@target 타입의 애노테이션을 전달 받는다.
@Before("allMember() && @target(annotation)")
//@within 타입의 애노테이션을 전달 받는다.
@Before("allMember() && @within(annotation)")
// @annotation : 메서드의 애노테이션을 전달 받는다.
@Before("allMember() && @annotation(annotation)")
- 매개변수 전달: this, target, args,@target, @within, @annotation, @args
- 포인트 컷의 이름과 매개변수의 이름을 맞춰야함
- 여기서는 arg로 맞춤
this vs target
- this: 스프링 빈 객체(프록시)를 대상으로 하는 조인 포인트
- target: 타겟 객체(가리키는 실제 대상)를 대상으로 하는 조인 포인트
- this는 프록시 생성 전략에 따라 JDK 동적 프록시가 나올수도, CGLIB이 나올수도 있음