2025. 6. 7. 16:32ㆍSpring Framework/Aspect Oriented Programming with Spring
🧩 Pointcut Expression 전체 구조:
execution(modifiers-pattern? return-type declaring-type? method-name-pattern(param-pattern) throws-pattern?)
🧪 Pointcut Expression 예제들
execution(
* // 리턴 타입: 아무 타입이나
transfer // 메서드 이름: transfer라는 이름
(..) // 파라미터: 개수와 타입 상관없이
)
🔍 세부 구성 설명
구성 요소 | 설명 |
---|---|
execution |
AOP의 표현식 종류 중 하나로, 메서드 실행 조인포인트를 지정합니다. Spring AOP는 이 execution() 만 지원합니다. |
* |
리턴 타입 패턴입니다. 별표(* )는 어떤 리턴 타입이든 허용한다는 의미입니다. 예: void , String , Account , 등 전부 포함됩니다. |
transfer |
정확한 메서드 이름입니다. 여기서는 transfer 라는 이름을 가진 메서드만 매칭됩니다. |
(..) |
파라미터 패턴입니다. - .. 은 0개 이상의 아무 타입의 파라미터를 의미합니다. - 예: transfer() , transfer(String) , transfer(Account, BigDecimal) 등 모두 포함됩니다. |
✅ 이 표현식이 매칭하는 대상 예시
다음과 같은 메서드들이 포인트컷에 매칭됩니다:
public void transfer() { }
public String transfer(String fromAccount, String toAccount) { }
protected Account transfer(Account a, BigDecimal amount) { }
단, 메서드 이름이 정확히 transfer
이어야만 합니다.
매칭된다는 의미
포인트컷 표현식이 해당 메서드를 조인포인트(Join Point)로 인식하여, 어드바이스(Advice)를 적용할 수 있는 대상이 된다는 의미입니다
❌ 이 표현식이 매칭되지 않는 대상 예시
public void transferMoney() { } // 이름이 다름
public void doTransfer(String a) { } // 이름이 다름
📌 참고: 자주 사용되는 변형 예시
표현식 | 의미 |
---|---|
execution(public * *(..)) |
모든 public 메서드 (리턴 타입, 이름, 파라미터 관계없이) |
execution(* com.example.service.*.*(..)) |
특정 패키지의 모든 클래스의 모든 메서드 |
execution(* transfer(..)) |
이름이 정확히 transfer 인 모든 메서드 |
execution(* *Transfer(..)) |
이름이 *Transfer 로 끝나는 메서드 |
execution(* transfer*(..)) |
이름이 transfer 로 시작하는 메서드 |
🧠 요약
요소 | 의미 |
---|---|
execution |
메서드 실행 조인포인트 지정 |
* |
리턴 타입 상관없음 |
transfer |
메서드 이름: 정확히 transfer |
(..) |
파라미터 개수/타입 상관없음 |
@Pointcut("within(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")
이 포인트컷은 다음 의미를 가집니다:
클래스 TransferService
안에 선언된 모든 메서드 실행(join point)에 대해 포인트컷을 설정한다.
즉, TransferService
클래스 내의 모든 메서드가 어드바이스의 적용 대상이 됩니다.
🧩 구성 요소 분석
구성 요소 | 의미 |
---|---|
@Pointcut(...) |
이 메서드는 AOP 포인트컷 시그니처로 사용됨 |
"within(...)" |
포인트컷 지시어 중 하나. 클래스 또는 패키지 범위로 "내부" 메서드들만 지정 |
com.intheeast.aspectjsupport.declaringpointcut.service.TransferService |
대상 클래스의 FQCN (Fully Qualified Class Name) |
✅ 예를 들어, 다음 클래스가 있다면:
package com.intheeast.aspectjsupport.declaringpointcut.service;
public class TransferService {
public void transferMoney() { }
public void logTransaction() { }
private void audit() { }
}
위 포인트컷은 다음 메서드들을 모두 타겟팅합니다:
public void transferMoney()
public void logTransaction()
private void audit()
← (Spring AOP는 기본적으로 public method만 프록시로 감쌈 — 주의)
⚠️ 주의 사항: within
vs execution
표현식 | 설명 |
---|---|
within(FQCN) |
클래스 내부의 모든 메서드를 타겟으로 삼음. 단, 상속된 메서드는 포함되지 않음 |
execution(* com.intheeast..TransferService.*(..)) |
해당 클래스의 메서드 실행뿐 아니라, 상속받은 메서드도 포함 |
📌 실무 팁: within
은 보통 "범위 필터"로 사용
within(...)
은 다음과 같은 경우에 자주 쓰입니다:
- 특정 패키지 또는 클래스 안에서만 AOP 적용하고 싶을 때
execution(...)
보다 더 넓은 범위를 잡고 싶은 경우
예:
@Pointcut("within(com.intheeast..*)") // 특정 루트 패키지 아래 모든 클래스의 모든 메서드
🎯 정리
항목 | 내용 |
---|---|
목적 | 특정 클래스 내부의 모든 메서드에 AOP를 적용하려는 포인트컷 |
표현식 | @Pointcut("within(fully.qualified.ClassName)") |
장점 | 코드 내 범위를 명확히 지정 가능 |
단점 | 상속 메서드 포함 안됨, 리턴타입·파라미터 조건 불가능 |
보완 | 세부 조정은 execution(...) 과 조합 |
@Pointcut("this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")
이 포인트컷은 this
포인트컷 지시어를 사용한 것으로, 프록시 객체의 타입을 기준으로 매칭합니다. execution
, within
, target
과는 완전히 다른 목적을 갖고 있으므로 그 차이를 이해하는 것이 중요합니다.
🔍 핵심 의미
현재 실행 중인 프록시 객체(this)의 런타임 타입이 TransferService
또는 그 하위 타입인 경우에 매칭된다.
즉, AOP 프록시 객체가 TransferService
타입이어야만 이 포인트컷에 매칭됩니다.
🧩 구성 요소 분석
요소 | 설명 |
---|---|
@Pointcut(...) |
AOP 포인트컷 시그니처 정의 |
"this(...)" |
프록시 객체의 실제 런타임 타입을 기준으로 조인포인트를 매칭 |
com.intheeast.aspectjsupport.declaringpointcut.service.TransferService |
프록시 객체가 이 타입일 때만 매칭됨 |
✅ 예제
대상 클래스
public interface TransferService {
void transfer();
}
@Component
public class TransferServiceImpl implements TransferService {
public void transfer() {
System.out.println("Transferring...");
}
}
AOP 설정
@Aspect
@Component
public class LoggingAspect {
@Pointcut("this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")
public void transferServiceProxy() {}
@Before("transferServiceProxy()")
public void log(JoinPoint jp) {
System.out.println("프록시가 TransferService 타입입니다: " + jp.getSignature());
}
}
결과
TransferServiceImpl
이 Spring AOP에 의해 프록시로 감싸져 있고, 그 프록시가TransferService
인터페이스를 구현하고 있다면 → 이 포인트컷이 매칭됩니다.- 즉, 프록시 객체가
TransferService
인터페이스 타입이어야 함
⚠️ 주의: this
vs target
표현식 | 기준 객체 | 설명 |
---|---|---|
this(...) |
프록시 객체의 타입 | 즉, Spring이 만들어준 AOP 프록시 객체가 TransferService 여야 매칭됨 |
target(...) |
실제 타겟 객체의 타입 | 프록시 뒤에 감싸진 실제 구현 클래스의 타입 기준으로 매칭 |
예: JDK 동적 프록시일 경우
프록시 타입: com.sun.proxy.$Proxy123 (인터페이스 기반)
this(인터페이스) → ✔️ 매칭
this(구현 클래스) → ❌ 안 됨
target(구현 클래스) → ✔️ 매칭
예: CGLIB일 경우
프록시 타입: TransferServiceImpl$$EnhancerBySpringCGLIB
this(구현 클래스) → ✔️ 매칭
this(인터페이스) → ✔️ 매칭
target(구현 클래스) → ✔️ 매칭
✅ 정리
항목 | 내용 |
---|---|
포인트컷 표현식 | this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService) |
기준 | 프록시 객체의 런타임 타입이 TransferService일 때 매칭 |
사용 목적 | **인터페이스 기반 프록시 (JDK 동적 프록시)**에 적합 |
차이점 | target() 은 실제 타겟 객체 기준, this() 는 프록시 기준 |
주의사항 | 프록시 생성 전략(JDK vs CGLIB)에 따라 매칭 여부가 달라짐 |
@Pointcut("target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService)")
이 포인트컷 표현식은,
현재 조인포인트(메서드 호출)의 실제 타겟 객체(프록시 뒤의 target 객체)의 런타임 타입이 SpecialService
타입이거나 그 하위 타입일 경우에 매칭된다.
즉, 프록시 뒤에 있는 실제 구현 객체의 타입을 기준으로 판단합니다.
🧩 구성 요소 분석
구성 요소 | 설명 |
---|---|
target(...) |
AOP 프록시가 호출을 위임하는 타겟 객체의 타입을 기준으로 매칭 |
SpecialService |
해당 타입이거나 그 하위 타입이면 매칭됨 |
@Pointcut(...) |
어드바이스에서 사용할 포인트컷 시그니처 정의 |
✅ 예제 코드
1. 대상 클래스
package com.intheeast.aspectjsupport.declaringpointcut.service;
public interface SpecialService {
void perform();
}
@Component
public class SpecialServiceImpl implements SpecialService {
public void perform() {
System.out.println("업무 실행 중...");
}
}
2. Aspect 클래스
@Aspect
@Component
public class LoggingAspect {
@Pointcut("target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService)")
public void specialServiceTarget() {}
@Before("specialServiceTarget()")
public void logBefore(JoinPoint jp) {
System.out.println("👉 타겟 객체 타입이 SpecialService: " + jp.getSignature());
}
}
🧪 실행 흐름
Spring AOP는 프록시 객체를 생성하고, 해당 프록시는 내부적으로 타겟 객체 (SpecialServiceImpl
) 에게 실제 호출을 위임합니다.
이때 target(...)
포인트컷은 실제 위임받는 타겟 객체(SpecialServiceImpl)의 타입이 SpecialService
인지 확인하여 어드바이스 적용 여부를 결정합니다.
⚠️ target
vs this
차이
항목 | this(...) |
target(...) |
---|---|---|
기준 | 프록시 객체의 타입 | 타겟 객체(프록시 뒤 실제 객체)의 타입 |
JDK 동적 프록시 | this(인터페이스) 만 가능 |
target(구현 클래스) 가능 |
CGLIB 프록시 | this(인터페이스 또는 클래스) 가능 |
target(구현 클래스) 가능 |
주의점 | 프록시 타입에 따라 매칭 안 될 수 있음 | 실제 객체 타입이 기준이므로 매칭률 높음 |
예시
SpecialService proxy = new SpecialServiceImpl(); // 프록시 객체
표현식 | 매칭 여부 (JDK 프록시 기준) |
---|---|
this(SpecialService) |
✅ 매칭됨 |
this(SpecialServiceImpl) |
❌ 매칭 안 됨 |
target(SpecialServiceImpl) |
✅ 매칭됨 |
🧠 정리
항목 | 내용 |
---|---|
포인트컷 표현식 | target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService) |
기준 | 실제 타겟 객체의 타입 |
사용 목적 | 구현 클래스 기반 매칭이 필요할 때 |
장점 | 프록시 생성 전략(JDK or CGLIB)에 관계없이 매칭 성공률 높음 |
실무 팁 | this() 는 인터페이스 기반 AOP일 때 주로 사용, target() 은 구현체 기준 판단 시 안전 |
@Pointcut("args(String, ..)")
이 포인트컷은 Spring AOP에서 메서드 호출 시점의 실제 아규먼트(=런타임 파라미터)의 타입을 기준으로 매칭하는 args
포인트컷입니다.
🔍 핵심 의미
이 포인트컷은 첫 번째 아규먼트가 String
타입이고, 그 뒤에 0개 이상의 어떤 아규먼트가 추가로 존재해도 상관없는 메서드를 타겟으로 지정합니다.
즉, 아래 조건을 만족하는 조인포인트(join point) 에만 어드바이스가 적용됩니다:
- 메서드를 실행할 때,
- 첫 번째 파라미터가
String
타입이고, - 그 뒤는 어떤 타입이든, 몇 개든 허용함
🧩 구성 요소 분석
표현식 | 의미 |
---|---|
args(...) |
런타임 시점에 메서드에 전달되는 실제 아규먼트의 타입으로 매칭 |
String, .. |
첫 번째 아규먼트는 String , 그 이후는 0개 이상의 아무 타입 |
✅ 예제 코드
AOP 설정
@Aspect
@Component
public class ArgumentAspect {
@Pointcut("args(String, ..)")
public void firstArgIsString() {}
@Before("firstArgIsString()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("📢 첫 번째 아규먼트가 String입니다: " + joinPoint.getSignature());
}
}
타겟 클래스
@Component
public class ExampleService {
public void greet(String name) {
System.out.println("Hello " + name);
}
public void greet(String name, int count) {
for (int i = 0; i < count; i++) {
System.out.println("Hello " + name);
}
}
public void test(int num, String str) {
System.out.println(num + ", " + str);
}
public void noArgs() {
System.out.println("Nothing");
}
}
매칭 결과
메서드 시그니처 | 매칭 여부 | 이유 |
---|---|---|
greet(String) |
✅ | 첫 번째 인자가 String |
greet(String, int) |
✅ | 첫 번째 인자가 String , 이후는 허용 |
test(int, String) |
❌ | 첫 번째 인자가 int |
noArgs() |
❌ | 인자가 없음 |
⚠️ 주의: args
vs execution
의 차이
표현식 | 기준 | 매칭 기준 |
---|---|---|
execution(* greet(String, ..)) |
정적(static) 시그니처 | 메서드의 정의에 따라 컴파일 시점에 판단 |
args(String, ..) |
동적(dynamic) 아규먼트 타입 | 런타임에 실제 아규먼트 타입을 기준으로 판단 |
즉, args(...)
는 런타임에 다형성을 반영할 수 있고, execution(...)
은 정확한 시그니처만 매칭합니다.
🎯 정리
항목 | 내용 |
---|---|
표현식 | args(String, ..) |
의미 | 첫 번째 아규먼트가 String 이고, 이후는 어떤 타입이든 허용 |
기준 | 런타임 시점에 전달된 아규먼트 타입 |
실무 활용 | 특정 타입의 입력 파라미터가 들어올 때만 로깅, 인증 등 적용 가능 |
비교 | args 는 동적 타입 검사 / execution 은 정적 타입 검사 |
@Pointcut("@annotation(com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable)")
이 포인트컷은 Spring AOP에서 애노테이션을 기준으로 어드바이스를 적용하기 위해 사용됩니다.
이 표현식은 매우 유용하며, 실제 실무에서 로깅, 보안, 트랜잭션 처리 등에 많이 활용됩니다.
✅ 핵심 의미
@Loggable
애노테이션이 붙은 메서드에 대해 AOP 어드바이스를 적용하라.
즉, 해당 포인트컷은 @Loggable
애노테이션이 선언된 메서드에만 어드바이스가 적용됩니다.
🧩 구성 요소 설명
요소 | 설명 |
---|---|
@Pointcut(...) |
포인트컷 시그니처를 정의하는 메서드 |
@annotation(...) |
조인포인트의 메서드에 붙은 애노테이션을 기준으로 매칭 |
com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable |
사용자 정의 애노테이션 (Fully Qualified Name) |
✅ 예제 코드 구성
1. 사용자 정의 애노테이션 @Loggable
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
@Target(ElementType.METHOD)
: 메서드에만 적용 가능@Retention(RetentionPolicy.RUNTIME)
: 런타임에 유지되어 AOP에서 접근 가능
2. 애노테이션을 사용하는 서비스
@Component
public class MyService {
@Loggable
public void performTask() {
System.out.println("작업 수행 중...");
}
public void doNothing() {
System.out.println("아무 일도 하지 않음");
}
}
3. Aspect 정의
@Aspect
@Component
public class LoggingAspect {
@Pointcut("@annotation(com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable)")
public void loggableMethods() {}
@Before("loggableMethods()")
public void logBefore(JoinPoint jp) {
System.out.println("🎯 @Loggable 애노테이션이 붙은 메서드 실행 전: " + jp.getSignature());
}
}
4. 실행 결과
[MyService.performTask()] 호출 시:
🎯 @Loggable 애노테이션이 붙은 메서드 실행 전: void performTask()
작업 수행 중...
[MyService.doNothing()] 호출 시:
아무 일도 하지 않음
(어드바이스 적용 안 됨)
🔍 @annotation
의 특징
항목 | 설명 |
---|---|
동작 시점 | 런타임 시, 메서드에 붙은 애노테이션을 검사 |
활용 사례 | 로깅, 감시(auditing), 보안 체크, 권한 검사 등 |
전달 가능성 | 어드바이스 메서드에 애노테이션 자체를 파라미터로 받을 수도 있음 |
@Before("@annotation(loggable)")
public void logBefore(JoinPoint jp, Loggable loggable) {
System.out.println("설명 추가: " + loggable.description());
}
단, 이 경우 @Loggable
안에 String description()
같은 필드가 있어야 합니다.
🧠 정리
항목 | 내용 |
---|---|
표현식 | @annotation(FQCN) |
대상 | 메서드에 지정된 애노테이션 |
활용도 | ★★★★★ (실무에서 가장 자주 쓰이는 유형 중 하나) |
장점 | 선언적 제어가 가능하므로 코드 분리성이 뛰어남 |
주의 | 클래스가 아닌 메서드 수준에만 적용됨 (클래스 레벨은 @within 사용) |
@Pointcut("@within(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
이 포인트컷은 Spring AOP의 고급 포인트컷 표현 중 하나인 @within
을 사용하는 경우입니다. 이 표현식이 정확히 어떤 조인포인트에 매칭되는지, 언제 쓰는지, @annotation
, @target
, @within
, @args
등과 어떤 차이가 있는지 전문가 수준으로 설명드리겠습니다.
✅ 핵심 의미
클래스에 @SpecialComponent
애노테이션이 선언되어 있다면,
그 클래스 안의 모든 메서드 실행 조인포인트에 AOP를 적용하라.
즉, 해당 포인트컷은 특정 애노테이션이 클래스에 붙어 있을 때, 그 클래스 안의 메서드들이 타겟이 됩니다.
🧩 구성 요소 설명
구성 요소 | 설명 |
---|---|
@Pointcut(...) |
포인트컷 시그니처 정의 |
@within(...) |
조인포인트의 선언 클래스에 지정된 애노테이션을 기준으로 매칭 |
com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent |
클래스에 선언된 사용자 정의 애노테이션 |
📦 예제 구성
1. 사용자 정의 애노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialComponent {
}
2. 서비스 클래스에 클래스 레벨 애노테이션 적용
@SpecialComponent
@Component
public class PaymentService {
public void process() {
System.out.println("결제 처리 중...");
}
public void cancel() {
System.out.println("결제 취소");
}
}
3. Aspect 정의
@Aspect
@Component
public class AuditingAspect {
@Pointcut("@within(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
public void auditedComponent() {}
@Before("auditedComponent()")
public void beforeAdvice(JoinPoint jp) {
System.out.println("🎯 클래스에 @SpecialComponent가 붙음: " + jp.getSignature());
}
}
✅ 실행 결과
[PaymentService.process()] 호출 시:
🎯 클래스에 @SpecialComponent가 붙음: void process()
결제 처리 중...
[PaymentService.cancel()] 호출 시:
🎯 클래스에 @SpecialComponent가 붙음: void cancel()
결제 취소
🔍 @within
vs 관련 표현식 비교
표현식 | 매칭 기준 | 대상 |
---|---|---|
@annotation(A) |
메서드에 애노테이션 A가 붙어 있어야 매칭 | 메서드 애노테이션 |
@within(A) |
클래스에 애노테이션 A가 붙어 있어야 매칭 | 클래스 애노테이션 |
@target(A) |
런타임 시 타겟 객체의 클래스에 A가 붙어 있어야 매칭 | 클래스 애노테이션 |
within(pkg.Class) |
클래스 내부의 모든 메서드 매칭 (애노테이션 관계 없음) | 클래스 기준 |
execution(...) |
정적 메서드 시그니처 기준 | 메서드 기준 |
🎯 정리
항목 | 설명 |
---|---|
표현식 | @within(FQCN of Annotation) |
의미 | 클래스에 해당 애노테이션이 붙어 있으면, 그 안의 모든 메서드가 타겟 |
사용 예 | 컴포넌트 스캔 범위 필터, 로깅, 보안, 감사 처리 |
장점 | 클래스 단위로 관심사를 적용할 수 있어 구조가 명확 |
주의 | 메서드 레벨이 아니라 클래스 레벨에 붙은 애노테이션이 기준 |
@Pointcut("@target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
이 포인트컷은 @target
포인트컷 디자인이터를 사용하는 것으로, 비슷하게 보이는 @within
과는 중요한 차이점이 있습니다.
✅ 핵심 의미
AOP 프록시의 타겟 객체의 클래스에 @SpecialComponent
애노테이션이 런타임에 존재하면, 그 객체의 모든 메서드 호출 조인포인트에 어드바이스를 적용하라.
즉,
- 클래스에
@SpecialComponent
가 붙어 있고, - 그 클래스가 Spring AOP에 의해 프록시로 감싸진 상태에서
- 해당 클래스의 메서드가 실행되면
→ 이 포인트컷이 매칭됩니다.
🧩 구성 요소 설명
구성 요소 | 설명 |
---|---|
@target(...) |
런타임 시점에 타겟 객체의 클래스에 지정된 애노테이션이 존재하는지 여부로 매칭 |
SpecialComponent |
사용자 정의 애노테이션 |
즉, 런타임 리플렉션을 통해 실제 프록시의 대상 객체 클래스에 애노테이션이 있는지를 검사합니다.
✅ 예제 코드
1. 사용자 정의 애노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialComponent {
}
2. 애노테이션이 붙은 서비스 클래스
@SpecialComponent
@Component
public class OrderService {
public void placeOrder() {
System.out.println("주문 처리 중...");
}
}
3. Aspect 클래스
@Aspect
@Component
public class MonitoringAspect {
@Pointcut("@target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
public void targetWithSpecialComponent() {}
@Before("targetWithSpecialComponent()")
public void beforeAdvice(JoinPoint jp) {
System.out.println("🎯 런타임 타겟 객체에 @SpecialComponent 존재함: " + jp.getSignature());
}
}
✅ 실행 결과
[OrderService.placeOrder()] 호출 시:
🎯 런타임 타겟 객체에 @SpecialComponent 존재함: void placeOrder()
주문 처리 중...
⚠️ @within
vs @target
차이점
표현식 | 검사 시점 | 기준 | 설명 |
---|---|---|---|
@within(A) |
컴파일 타임 기준 | 클래스 선언부 | 클래스에 A 애노테이션이 "정적으로" 붙어 있는지 |
@target(A) |
런타임 기준 | 실제 타겟 객체의 클래스 | 프록시의 대상 객체가 런타임에 A 애노테이션을 "보유"하고 있는지 |
예외 상황 (동적 프록시 활용 시)
@within
은 클래스의 컴파일 정의 기준이므로 항상 동일@target
은 런타임 프록시 객체가 다른 객체를 감쌀 경우, 그 감싸진 객체에 따라 매칭 결과가 달라질 수 있음
🧠 요약
항목 | 설명 |
---|---|
표현식 | @target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent) |
적용 대상 | 런타임 기준으로 실제 타겟 객체의 클래스에 @SpecialComponent 가 붙어 있는 경우 |
사용 목적 | 런타임에서 AOP가 적용될지 동적으로 판단하고 싶을 때 |
차이점 | @within : 정적 클래스 기반 / @target : 동적 런타임 기반 |
📌 실전 팁
상황 | 추천 포인트컷 |
---|---|
선언된 클래스에 따라 적용 | @within(...) |
런타임에서 동적으로 적용하고 싶을 때 | @target(...) |
메서드에 직접 애노테이션 붙인 경우 | @annotation(...) |
@Pointcut("@args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated)")
이 포인트컷은 Spring AOP의 고급 포인트컷 표현식 중 하나로, @args
지시어를 사용합니다. 이 표현식은 실무에서 자주 쓰이지는 않지만, 파라미터 객체의 타입에 선언된 애노테이션을 기준으로 조인포인트를 매칭하는 매우 정교한 조건을 제공합니다.
✅ 핵심 의미
메서드 호출 시 전달된 실제 아규먼트(파라미터 객체)의 런타임 클래스가 @Validated
애노테이션이 붙은 타입일 경우에만 포인트컷이 매칭된다.
즉,
- 메서드에 전달된 아규먼트 중
- 그 타입의 클래스에
@Validated
애노테이션이 붙어 있을 경우 - 어드바이스가 적용된다.
🔍 용어 정리
용어 | 의미 |
---|---|
args(...) |
전달된 런타임 아규먼트의 타입을 기준으로 매칭 |
@args(...) |
전달된 런타임 아규먼트의 타입에 붙은 애노테이션을 기준으로 매칭 |
📦 예제 코드 구성
1. 사용자 정의 애노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Validated {
}
2. @Validated
애노테이션이 붙은 DTO
@Validated
public class PaymentRequest {
private String cardNumber;
private int amount;
// getters/setters
}
3. 타겟 서비스
@Component
public class PaymentService {
public void process(PaymentRequest request) {
System.out.println("결제 처리: " + request.getCardNumber());
}
public void test(String name) {
System.out.println("단순 문자열 처리: " + name);
}
}
4. Aspect 정의
@Aspect
@Component
public class ValidationAspect {
@Pointcut("@args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated)")
public void validatedArgs() {}
@Before("validatedArgs()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("✅ 전달된 객체 타입에 @Validated 애노테이션이 붙어 있음: " + joinPoint.getSignature());
}
}
✅ 실행 결과
paymentService.process(new PaymentRequest());
// 출력: ✅ 전달된 객체 타입에 @Validated 애노테이션이 붙어 있음
paymentService.test("hello");
// 출력 없음 (매칭 안 됨)
⚠️ 주의 사항
항목 | 설명 |
---|---|
검사 대상 | 아규먼트의 런타임 타입 클래스에 선언된 애노테이션 |
비교 대상 | @annotation(...) 은 메서드에 붙은 애노테이션을 검사하고, @args(...) 는 파라미터 객체 타입에 선언된 애노테이션을 검사 |
JDK 프록시의 경우 | @args(...) 는 파라미터 객체가 인터페이스일 경우 동작하지 않을 수 있음. 실제 클래스에 애노테이션이 선언되어 있어야 함 |
✅ 요약 비교표
표현식 | 설명 |
---|---|
@annotation(A) |
메서드에 애노테이션 A가 선언됨 |
@within(A) |
클래스에 애노테이션 A가 선언됨 |
@target(A) |
런타임 타겟 객체의 클래스에 A가 선언됨 |
@args(A) |
파라미터 객체의 타입에 애노테이션 A가 선언됨 |
🧠 정리
항목 | 내용 |
---|---|
포인트컷 표현식 | @args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated) |
매칭 기준 | 런타임에 전달된 아규먼트의 클래스에 @Validated 애노테이션이 붙어 있는지 |
실용 사례 | DTO에 @Validated , @Secure , @Sensitive 같은 도메인 애노테이션 붙이고, 자동으로 검증 또는 감시 적용 |
주의 | 단일 아규먼트만 매칭하거나, 여러 아규먼트 중 일부에만 적용되면 포인트컷이 매칭되지 않을 수 있음 |
'Spring Framework > Aspect Oriented Programming with Spring' 카테고리의 다른 글
@EnableAspectJAutoProxy (1) | 2025.06.09 |
---|---|
Aspect Oriented Programming with Spring (1) | 2024.11.17 |
Choosing which AOP Declaration Style to Use (0) | 2024.11.17 |
Proxying Mechanisms (1) | 2024.11.17 |
Introductions (1) | 2024.11.17 |