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 |