SpringStudy

Pointcut 문법 정리

dding-shark 2025. 8. 20. 16:04
728x90

Spring AOP 포인트컷 문법 정리

개요

포인트컷(Pointcut)은 Advice가 적용될 조인 포인트를 선별하는 기준을 정의합니다. Spring AOP에서는 다양한 방식으로 포인트컷을 정의할 수 있으며, 각각의 특징과 사용법을 이해하는 것이 중요합니다.

1. 정적 메서드 매처 포인트컷 (StaticMethodMatcherPointcut)

개념

컴파일 타임에 메서드 이름과 클래스 타입만으로 매칭을 결정하는 포인트컷입니다.

문법 구조

public class CustomStaticPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        // 메서드 매칭 로직
        return condition;
    }

    @Override
    public ClassFilter getClassFilter() {
        // 클래스 필터링 로직
        return cls -> condition;
    }
}

실제 예제

public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return "sing".equals(method.getName());  // sing 메서드만 매칭
    }

    @Override
    public ClassFilter getClassFilter() {
        return cls -> cls == GoodGuitarist.class;  // 특정 클래스만 대상
    }
}

특징

  • 성능: 매우 우수 (한번 계산되면 캐시됨)
  • 유연성: 제한적 (런타임 매개변수 고려 불가)
  • 사용 시점: 메서드명이나 클래스 기반 필터링

2. 동적 메서드 매처 포인트컷 (DynamicMethodMatcherPointcut)

개념

런타임에 메서드 매개변수까지 고려하여 매칭을 결정하는 포인트컷입니다.

문법 구조

public class CustomDynamicPointcut extends DynamicMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        // 1차 정적 필터링
        return staticCondition;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        // 2차 동적 필터링 (런타임 매개변수 사용)
        return dynamicCondition;
    }

    @Override
    public ClassFilter getClassFilter() {
        return cls -> condition;
    }
}

실제 예제

public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return "foo".equals(method.getName());  // foo 메서드만 1차 통과
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        int x = ((Integer) args[0]).intValue();
        return x != 100;  // 첫 번째 매개변수가 100이 아닌 경우만 통과
    }

    @Override
    public ClassFilter getClassFilter() {
        return cls -> cls == SampleBean.class;
    }
}

특징

  • 성능: 낮음 (매번 런타임 검사)
  • 유연성: 매우 높음 (매개변수 기반 조건)
  • 사용 시점: 매개변수에 따른 조건부 적용

3. 이름 매칭 포인트컷 (NameMatchMethodPointcut)

개념

메서드 이름 패턴을 사용하여 매칭하는 Spring 내장 포인트컷입니다.

문법 구조

NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("methodName");       // 정확한 이름 매칭
pointcut.addMethodName("method*");          // 와일드카드 사용
pointcut.setMappedNames("name1", "name2");  // 여러 이름 설정

실제 예제

// 사용법 1: 개별 메서드 추가
NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
pc.addMethodName("sing");
pc.addMethodName("rest");

// 사용법 2: 배열로 한번에 설정
NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
pc.setMappedNames("sing", "rest", "talk");

// 와일드카드 사용
pc.addMethodName("get*");    // get으로 시작하는 모든 메서드
pc.addMethodName("*ing");    // ing로 끝나는 모든 메서드

패턴 문법

  • *: 0개 이상의 문자와 매칭
  • ?: 정확히 1개 문자와 매칭
  • 대소문자 구분

특징

  • 성능: 우수
  • 사용성: 매우 간편
  • 유연성: 기본적인 패턴 매칭만 지원

4. 정규식 포인트컷 (JdkRegexpMethodPointcut)

개념

Java 정규식을 사용하여 메서드 이름을 매칭하는 포인트컷입니다.

문법 구조

JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern("정규식패턴");
// 또는
pointcut.setPatterns("패턴1", "패턴2", "패턴3");

실제 예제

JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();

// 기본 패턴
pc.setPattern(".*sing.*");           // sing이 포함된 모든 메서드
pc.setPattern("get.*");              // get으로 시작하는 메서드
pc.setPattern(".*\\.sing");          // 정확히 sing으로 끝나는 메서드

// 복잡한 패턴
pc.setPattern("(get|set).*");        // get 또는 set으로 시작
pc.setPattern(".*[Ss]ing.*");        // Sing 또는 sing이 포함된 메서드

// 여러 패턴 동시 적용
pc.setPatterns(".*sing.*", ".*play.*", ".*rest.*");

정규식 패턴 문법

  • .: 임의의 한 문자
  • *: 앞 문자가 0회 이상 반복
  • +: 앞 문자가 1회 이상 반복
  • ?: 앞 문자가 0회 또는 1회
  • ^: 문자열 시작
  • $: 문자열 끝
  • []: 문자 클래스
  • (): 그룹핑
  • |: OR 연산

특징

  • 성능: 보통 (정규식 엔진 사용)
  • 유연성: 매우 높음
  • 복잡성: 높음 (정규식 지식 필요)

5. AspectJ 표현식 포인트컷 (AspectJExpressionPointcut)

개념

AspectJ의 강력한 포인트컷 표현식을 Spring AOP에서 사용할 수 있게 해주는 포인트컷입니다.

문법 구조

AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("AspectJ 표현식");

기본 지시자 (Designators)

execution

메서드 실행 조인 포인트를 매칭합니다.

// 문법: execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)

pointcut.setExpression("execution(public * *(..))");              // 모든 public 메서드
pointcut.setExpression("execution(* com.example.service.*.*(..))"); // 특정 패키지의 모든 메서드
pointcut.setExpression("execution(* *..service.*Service.*(..))");   // Service로 끝나는 클래스의 모든 메서드
pointcut.setExpression("execution(String com.example.*.get*())");   // String을 반환하는 get으로 시작하는 메서드
pointcut.setExpression("execution(* sing*(..))");                   // sing으로 시작하는 모든 메서드

args

메서드 매개변수 타입을 매칭합니다.

pointcut.setExpression("args(String)");                    // String 타입 매개변수 1개
pointcut.setExpression("args(String, int)");               // String, int 순서의 매개변수 2개
pointcut.setExpression("args(String, ..)");                // 첫 번째가 String이고 나머지는 임의
pointcut.setExpression("args(..)");                        // 임의의 매개변수

target

타겟 객체의 타입을 매칭합니다.

pointcut.setExpression("target(com.example.service.UserService)");  // 특정 타입의 타겟
pointcut.setExpression("target(org.springframework.stereotype.Service)"); // 특정 어노테이션이 붙은 타겟

this

프록시 객체의 타입을 매칭합니다.

pointcut.setExpression("this(com.example.service.UserService)");

bean (Spring 확장)

Spring 빈 이름을 매칭합니다.

pointcut.setExpression("bean(userService)");        // 특정 빈 이름
pointcut.setExpression("bean(*Service)");           // Service로 끝나는 빈
pointcut.setExpression("bean(user*)");              // user로 시작하는 빈

조합 연산자

// AND 연산 (&&)
pointcut.setExpression("execution(* sing*(..)) && args(Guitar)");

// OR 연산 (||)
pointcut.setExpression("execution(* sing*(..)) || execution(* play*(..))");

// NOT 연산 (!)
pointcut.setExpression("execution(* *(..)) && !execution(* toString())");

// 복잡한 조합
pointcut.setExpression("(execution(* sing*(..)) || execution(* play*(..))) && target(Guitarist)");

실제 예제

AspectJExpressionPointcut pc = new AspectJExpressionPointcut();

// 기본 사용
pc.setExpression("execution(* sing*(..))");

// 패키지 지정
pc.setExpression("execution(* com.springstudy.playground.aop.ch5_13_NameMatchMethodPointcut.*.*(..))");

// 매개변수 조건
pc.setExpression("execution(* *(com.springstudy.playground.aop.ch5_13_NameMatchMethodPointcut.Guitar))");

// 복합 조건
pc.setExpression("execution(* sing*(..)) && bean(john*)");

특징

  • 성능: 우수 (내부적으로 최적화됨)
  • 유연성: 매우 높음
  • 표현력: 가장 강력함
  • 학습 비용: 높음

6. 어노테이션 매칭 포인트컷 (AnnotationMatchingPointcut)

개념

메서드나 클래스에 특정 어노테이션이 붙은 경우를 매칭하는 포인트컷입니다.

문법 구조

// 메서드 어노테이션 매칭
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(어노테이션클래스.class);

// 클래스 어노테이션 매칭
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(어노테이션클래스.class);

// 클래스와 메서드 어노테이션 모두 매칭
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(클래스어노테이션.class, 메서드어노테이션.class);

커스텀 어노테이션 정의

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdviceRequired {
    String value() default "";
    int priority() default 0;
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitored {
    boolean enabled() default true;
}

실제 예제

// 메서드 어노테이션 기반
AnnotationMatchingPointcut pc1 = 
    AnnotationMatchingPointcut.forMethodAnnotation(AdviceRequired.class);

// 클래스 어노테이션 기반
AnnotationMatchingPointcut pc2 = 
    AnnotationMatchingPointcut.forClassAnnotation(Service.class);

// 조합 사용
AnnotationMatchingPointcut pc3 = 
    new AnnotationMatchingPointcut(Monitored.class, AdviceRequired.class);

// AspectJ 표현식으로도 가능
AspectJExpressionPointcut pc4 = new AspectJExpressionPointcut();
pc4.setExpression("@annotation(com.example.AdviceRequired)");
pc4.setExpression("@target(org.springframework.stereotype.Service)");

어노테이션 속성 활용

// 어노테이션 속성 기반 조건
@Before("@annotation(adviceRequired)")
public void handleAdvice(JoinPoint joinPoint, AdviceRequired adviceRequired) {
    if (adviceRequired.priority() > 5) {
        // 높은 우선순위 처리
    }
}

특징

  • 가독성: 매우 높음 (선언적)
  • 유지보수성: 우수
  • 타입 안전성: 높음
  • 설정 복잡도: 낮음

7. 포인트컷 조합 (ComposablePointcut)

개념

여러 포인트컷을 논리 연산자로 조합하여 복합 조건을 만드는 방법입니다.

문법 구조

ComposablePointcut composable = new ComposablePointcut();

// Union (OR 연산)
composable.union(다른포인트컷);

// Intersection (AND 연산)
composable.intersection(다른포인트컷);

// 체이닝도 가능
composable.union(포인트컷1).intersection(포인트컷2);

실제 예제

// 기본 포인트컷들 정의
StaticMethodMatcher singMatcher = new StaticMethodMatcher() {
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().startsWith("si");
    }
};

StaticMethodMatcher restMatcher = new StaticMethodMatcher() {
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().endsWith("st");
    }
};

// 조합 포인트컷 생성
ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, singMatcher);

// Union 연산 (OR): sing으로 시작하거나 st로 끝나는 메서드
pc.union(restMatcher);

// Intersection 연산 (AND): 추가 조건 적용
StaticMethodMatcher talkMatcher = new StaticMethodMatcher() {
    public boolean matches(Method method, Class<?> targetClass) {
        return "talk".equals(method.getName());
    }
};
pc.intersection(talkMatcher);

정적 메서드 활용

// ComposablePointcut의 정적 메서드 사용
ComposablePointcut pc1 = ComposablePointcut.union(포인트컷1, 포인트컷2);
ComposablePointcut pc2 = ComposablePointcut.intersection(포인트컷1, 포인트컷2);

특징

  • 재사용성: 높음
  • 조합 가능성: 무한대
  • 복잡성: 조합에 따라 증가
  • 디버깅: 복잡한 조합은 어려움

8. 성능 비교 및 선택 가이드

성능 순서 (빠름 → 느림)

  1. StaticMethodMatcherPointcut: 가장 빠름
  2. NameMatchMethodPointcut: 매우 빠름
  3. AspectJExpressionPointcut: 빠름
  4. JdkRegexpMethodPointcut: 보통
  5. AnnotationMatchingPointcut: 보통
  6. DynamicMethodMatcherPointcut: 느림

선택 가이드

단순한 메서드명 매칭

// 권장: NameMatchMethodPointcut
NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
pc.addMethodName("get*");

복잡한 조건이 필요한 경우

// 권장: AspectJExpressionPointcut
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
pc.setExpression("execution(* com.example.service.*.*(..)) && @annotation(Transactional)");

런타임 매개변수 검사

// 권장: DynamicMethodMatcherPointcut (불가피한 경우만)
public class ParameterBasedPointcut extends DynamicMethodMatcherPointcut {
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return args.length > 0 && args[0] instanceof String;
    }
}

선언적 방식 선호

// 권장: AnnotationMatchingPointcut + 커스텀 어노테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable { }

AnnotationMatchingPointcut pc = 
    AnnotationMatchingPointcut.forMethodAnnotation(Cacheable.class);

9. 실무 활용 패턴

서비스 레이어 트랜잭션

AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
pc.setExpression("execution(* com.example.service.*.*(..)) && @target(org.springframework.stereotype.Service)");

컨트롤러 로깅

AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
pc.setExpression("execution(* com.example.controller.*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)");

캐시 적용

AnnotationMatchingPointcut pc = AnnotationMatchingPointcut.forMethodAnnotation(Cacheable.class);

보안 검사

AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
pc.setExpression("execution(* com.example.service.*.*(..)) && @annotation(com.example.security.RequiresPermission)");

10. 디버깅 및 테스트

포인트컷 매칭 테스트

@Test
public void testPointcutMatching() {
    SimpleStaticPointcut pointcut = new SimpleStaticPointcut();

    Method singMethod = GoodGuitarist.class.getMethod("sing");
    Method restMethod = GoodGuitarist.class.getMethod("rest");

    assertTrue(pointcut.matches(singMethod, GoodGuitarist.class));
    assertFalse(pointcut.matches(restMethod, GoodGuitarist.class));
}

AspectJ 표현식 검증

@Test
public void testAspectJExpression() {
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression("execution(* sing*(..))");

    Method method = TestClass.class.getMethod("singLoudly");
    assertTrue(pointcut.matches(method, TestClass.class));
}

런타임 디버깅

public class DebuggingPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        boolean result = method.getName().startsWith("sing");
        System.out.println("포인트컷 매칭: " + method.getName() + " -> " + result);
        return result;
    }
}
728x90

'SpringStudy' 카테고리의 다른 글

Spring Hibernate  (7) 2025.08.26
JDBC 최종  (4) 2025.08.21
JDBC 학습 정리(1)  (0) 2025.08.21
SpringAop -final  (0) 2025.08.20
Spring AOP  (1) 2025.08.19