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. 성능 비교 및 선택 가이드
성능 순서 (빠름 → 느림)
- StaticMethodMatcherPointcut: 가장 빠름
- NameMatchMethodPointcut: 매우 빠름
- AspectJExpressionPointcut: 빠름
- JdkRegexpMethodPointcut: 보통
- AnnotationMatchingPointcut: 보통
- 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 |