Spring/Spring

[Spring][AOP] 2. Pointcut API

noahkim_ 2025. 4. 9. 17:25

1. Spring Pointcut API

  • 어떤 Advice가 어떤 메서드에 적용될지를 결정하는 기준을 정의함

 

Pointcut 인터페이스

public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}
  • Advice의 적용 대상을 지정
  • 클래스와 메서드 조건이 모두 만족될 때 적용됨

 

항목 ClassFilter MethodMatcher
역할 Pointcut이 적용될 클래스를 필터링
Pointcut이 적용될 메서드를 필터링
평가 시점 AOP 프록시 생성 시점
정적: 프록시 생성 시
동적: 메서드 호출 시마다 평가
정적/동적 여부 항상 정적
정적 (isRuntime() == false)
동적 (isRuntime() == true)
주요 사용 목적 클래스 필터링 (특정 패키지, 클래스 이름 등)
메서드 필터링 (메서드 이름, 시그니처, 인자 등)
성능 성능 부담 적음
정적은 성능 부담 적음
동적은 호출 시마다 평가되어 성능 부담 있음

 

예시) ClassFilter

더보기
더보기
public class CustomClassFilter implements ClassFilter {
    
    @Override
    public boolean matches(Class<?> clazz) {
        return clazz.getPackageName().startsWith("org.example");
    }
}

 

예시) MethodMatcher

더보기
더보기

정적

public class SetterMethodMatcher implements MethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().startsWith("set");
    }

    @Override
    public boolean isRuntime() {
        return false; // 정적 매처이므로 false 반환
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return false; // 정적 매처이므로 사용 안함
    }
}

 

동적

public class FirstArgStringMatcher implements MethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return true; // 일단 매칭 통과 (런타임에 판단하므로 true 반환)
    }

    @Override
    public boolean isRuntime() {
        return true;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return args.length > 0 && args[0] instanceof String; // 첫 번째 인자가 String일 경우만 매칭
    }
}

 

연산

연산 종류 설명
합집합 두 pointcut 중 하나라도 일치하면 적용됨
교집합 두 pointcut이 모두 일치해야 적용됨

예제

더보기
더보기
ClassFilter serviceClassFilter = new ServiceClassFilter();
MethodMatcher firstArgStringMatcher = new FirstArgStringMatcher();

Pointcut intersectionPointcut = Pointcuts.intersection(serviceClassFilter, firstArgStringMatcher);
Pointcut unionPointcut = Pointcuts.union(serviceClassFilter, firstArgStringMatcher);

 

2. AspectJ Expression Pointcut

  • AspectJ 표현식 문자열을 사용하여 포인트컷을 정의
  • org.springframework.aop.aspectj.AspectJExpressionPointcut 클래스
  • 가장 많이 사용하는 방식

 

표현 방식

종류 설명
execution 특정 클래스 내 모든 메서드 실행 시점
within 특정 패키지 내 모든 클래스의 메서드 실행 시점
this 프록시 객체가 주어진 타입일 때
target 실제 대상 객체가 주어진 타입일 때
args 인자 타입 기반 지정
@target 클래스에 특정 애노테이션이 있을 때 (런타임 기준)
@within 클래스에 특정 애노테이션이 있을 때 (컴파일 타임 기준: 상속 X)
@annotation 메서드에 특정 애노테이션이 있을 때
@args 인자에 특정 애노테이션이 있을 때

예제

더보기
더보기

execution

@Pointcut("execution(* com.example.service.OrderService.*(..))")
public void allOrderServiceMethods() { ... }

 

within

@Pointcut("within(com.example.repository..*)")
public void allRepositoryMethods() { ... }

 

this

@Pointcut("this(com.example.MyInterface)")
public void proxyImplementMyInterface() { ... }

 

target

@Pointcut("target(com.example.impl.MyInterfaceImpl)")
public void targetIsMyServiceImpl() { ... }

 

args

@Pointcut("args(java.lang.String)")
public void methodTakesStringArg() { ... }

 

@target

@Pointcut("@target(org.springframework.stereotype.Service)")
public void classAnnotatedWithService() { ... }

 

@within

@Pointcut("@within(org.springframework.stereotype.Repository)")
public void repoClassMethods() {}

 

@annotation

@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}

 

@args

@Pointcut("@args(jakarta.validation.Valid)")
public void validArgsMethods() {}

 

3. Pointcut 구현체

Static Pointcut

구현체 클래스 이름 설명
JdkRegexpMethodPointcut 메서드 이름을 정규식으로 매칭하는 포인트컷
RegexpMethodPointcutAdvisor 포인트컷 + 어드바이스를 함께 가지는 어드바이저

 

예제) JdkRegexpMethodPointcut

더보기
더보기
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns("com.example.service.*Service.*");

예제) RegexpMethodPointcutAdvisor

더보기
더보기
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
advisor.setPatterns("com.example.service.*Service.*");
advisor.setAdvice(myAdvice);
RegexpMethodPointcutAdvisor advisor =
    new RegexpMethodPointcutAdvisor("com.example.service.*Service.*", myAdvice);

 

Dynamic Pointcut

구현체 클래스 이름 설명
DynamicMethodMatcherPointcut 런타임 시점에 인자를 기준으로 포인트컷 결정
ControlFlowPointcut 호출 스택에서 특정 클래스/메서드가 포함되어 있을 때만 포인트컷 매칭
  • 일반적인 정적 포인트컷보다 성능은 떨어지지만, 더 유연한 제어가 가능합니다.

 

예제) DynamicMethodMatcherPointcut

더보기
더보기
public class FirstArgIsAdminPointcut extends DynamicMethodMatcherPointcut {

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        // static 검사 - 일단 모두 true 반환 (dynamic 검사로 넘김)
        return true;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        if (args.length > 0 && args[0] instanceof String) {
            return "admin".equals(args[0]);
        }
        return false;
    }
}

예제) ControlFlowPointcut

더보기
더보기
public class ControlFlowPointcutExample {

    public static void main(String[] args) {
        ControlFlowPointcut pointcut = new ControlFlowPointcut(SomeCaller.class);

        System.out.println("From outside: " + pointcut.matches(MyService.class.getMethods()[0], MyService.class)); // false

        new SomeCaller().call(); // 내부 호출로 인해 advice 적용됨
    }

    static class MyService {
        public void doSomething() {
            System.out.println("Doing something");
        }
    }

    static class SomeCaller {
        public void call() {
            new MyService().doSomething(); // 이 호출은 ControlFlowPointcut에 의해 매칭됨
        }
    }
}

 

Attribute-driven Pointcut

구현체 클래스 이름 설명
StaticMethodMatcherPointcut (기반 구현)
정적으로 메서드 매칭, 보통 AnnotationMatchingPointcut의 슈퍼타입으로 쓰임
AnnotationMatchingPointcut
클래스 또는 메서드에 지정된 어노테이션으로 매칭
ComposablePointcut
여러 포인트컷 조합 (합집합, 교집합, 차집합 등) 지원
AspectJAnnotationPointcut
@Aspect 기반 AOP에서 사용되는 AspectJ 스타일의 표현식을 처리하는 포인트컷

 

예제) AnnotationMatchingPointcut

더보기
더보기
// 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Loggable {}

// 타깃 클래스
public class MyService {
    @Loggable
    public void serve() {
        System.out.println("Serving...");
    }
}
// 포인트컷 설정
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(null, Loggable.class);

// Advice는 MethodInterceptor로 구현
Advisor advisor = new DefaultPointcutAdvisor(pointcut, (MethodInterceptor) invocation -> {
    System.out.println("Before method");
    Object result = invocation.proceed();
    System.out.println("After method");
    return result;
});

 

예제) ComposablePointcut

더보기
더보기
// 클래스 필터: MyService 클래스에만 적용
ClassFilter classFilter = clazz -> clazz == MyService.class;

// 메서드 필터: 메서드 이름이 "serve"
MethodMatcher methodMatcher = new StaticMethodMatcher() {
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().equals("serve");
    }
};

ComposablePointcut pointcut = new ComposablePointcut(classFilter, methodMatcher);

// 또 다른 조건 추가: 메서드 이름이 "assist"
MethodMatcher secondMatcher = new StaticMethodMatcher() {
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().equals("assist");
    }
};

// 두 조건을 union (serve 또는 assist)
pointcut = pointcut.union(secondMatcher);

 

예제) AspectJAnnotationPointcut

더보기
더보기
@Aspect
@Component
public class LoggingAspect {

    // 모든 service 패키지의 메서드에 적용
    @Before("execution(* com.example.service..*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("AspectJ log: " + joinPoint.getSignature().getName());
    }
}
// 내부적으로 AspectJAnnotationPointcut이 사용됨
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.example.service..*(..))");

 

4. Custom Pointcut

  • 정적 또는 동적으로 직접 구현 가능
  • 자유도가 높지만, 가능하면 AspectJ 표현식을 사용하는 것이 추천됨

 

 

출처

'Spring > Spring' 카테고리의 다른 글

[Spring][AOP] 3. Advisor API  (0) 2025.04.09
[Spring][AOP] 1. Advice API  (0) 2025.04.09
[Spring][Validation] 2. Java Bean Validation  (0) 2025.04.06
[Spring][Validation] 1. Validator Interface  (0) 2025.04.06
[Spring][Object] 1. Data Binding  (0) 2025.04.06