스프링 AOP에서 빠질 수 없는 개념으로 포인트컷, 어드바이스, 어드바이저가 있다.
포인트컷(Pointcut): 어디에 부가 기능을 적용할지, 적용하지 않을 것인지 판단하는 필터링 로직
- 주로 클래스와 메서드 이름으로 필터링한다.
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
포인트컷은 크게 둘로 이루어진다.
- ClassFilter : 적용하려는 클래스가 맞는지 확인
- MethodMatcher : 적용하려는 메서드가 맞는지 확인일반적으로는 스프링이 제공하는 구현체를 사용한다.
- 둘 다 true 로 반환해야 어드바이스를 적용할 수 있다.
- 일반적으로는 스프링이 제공하는 구현체를 사용한다.
어드바이스(Advice): 프록시에 적용하는 부가 기능(프록시 로직)
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
MethodInterceptor는 Interceptor를 상속하고 Interceptor는 Advice 인터페이스를 상속하므로 위 인터페이스를 구현하면 Advice를 만들 수 있다.
invoke() 메서드 내부에는 MethodInvocation invocation를 매개변수로 받는데 내부에는 다음 메서드를 호출하는 방법이나 현재 프록시 객체 인스턴스, args, 메서드 정보 등 많은 정보가 포함되어 있다.
즉 JDK 동적 프록시의 핸들러나, CGLIB의 인터셉터에서 매개변수로 제공되는 부분들이 모두 포함된 객체라고 생각하면 된다.
@Slf4j
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("TimeProxy 실행");
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy 종료 resultTime={}ms", resultTime);
return result;
}
}
어드바이저(Advisor): 하나의 포인트컷과 하나의 어드바이스를 가진 오브젝트
@Test
void advisorTest() {
ServiceImpl target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("save");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
}
위 코드의 흐름은 다음과 같다.
- 프록시 팩토리를 생성
- NameMatchMethodPointcut는 스프링이 제공하는 Pointcut의 구현체로 메서드 이름을 가지고 어드바이스 적용 여부를 확인한다.
- 위 코드에서는 setMappedNames("save")를 통해 save만 어드바이스가 적용되도록 하였다.
- 따라서 save 메서드는 어드바이스가 적용되지만 find는 적용되지 않는다.
- DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice())
- 하나의 포인트컷과 하나의 어드바이스를 가진 어드바이저를 생성한다.
- proxyFactory.addAdvisor(advisor)
- 프록시 팩토리에 적용할 어드바이저를 지정한다.
- 어드바이저는 내부에 포인트컷과 어드바이스를 모두 가지고 있으므로 따라서 어디에 어떤 부가 기능을 적용해야 할 지 알 수 있다.
- 프록시 팩토리를 사용할 때 어드바이저는 필수
- 프록시 팩토리를 통해 프록시 생성
- 프록시의 save, find 메서드 호출
- save는 포인트컷이 true이므로 어드바이스 적용, find는 포인트컷이 false이므로 어드바이스가 적용되지 않음
- 실제 타겟의 메서드 호출
어드바이저는 하나의 포인트컷과 하나의 어드바이스를 가진다고 했는데, 만약 대상 객체(target)에 여러 어드바이스를 적용하려면 어떻게 해야할까?
스프링은 하나의 프록시 팩토리에 여러 어드바이저를 적용할 수 있도록 하였다.
@Test
void multiAdvisorTest() {
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory1 = new ProxyFactory(target);
proxyFactory1.addAdvisor(advisor2);
proxyFactory1.addAdvisor(advisor1);
ServiceInterface proxy = (ServiceInterface) proxyFactory1.getProxy();
//proxy -> advisor2 -> advisor1 -> target
proxy.save();
}
여러 어드바이저를 적용하고 싶다면 프록시 팩토리에 addAdvisor()를 통해서 여러 어드바이저를 등록하면 된다. 등록하는 순서대로 advisor가 호출된다.
→ AOP 적용 수 만큼 프록시가 생성되는 것이 아니라 프록시는 하나만 만들고, 하나의 프록시에 여러 어드바이저를 적용한다.
스프링이 제공하는 포인트컷
- NameMatchMethodPointcut : 메서드 이름을 기반으로 매칭
- 내부에서는 PatternMatchUtils를 사용 → xxx 허용
- JdkRegexpMethodPointcut : JDK 정규 표현식을 기반으로 포인트컷 매칭
- TruePointcut : 항상 참 반환
- AnnotationMatchingPointcut : 애노테이션으로 매칭
- AspectJExpressionPointcut : aspectJ 표현식으로 매칭
참고
스프링 핵심 원리 - 고급편 강의 | 김영한 - 인프런
김영한 | 스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., 핵심 디자인 패턴, 쓰레드 로컬, 스프링 AOP스프링의 3가지 핵심 고급 개념 이해하기
www.inflearn.com
'Spring' 카테고리의 다른 글
[Spring AOP] 스프링 AOP와 용어 (0) | 2024.12.28 |
---|---|
[Spring AOP] 빈 후처리기 (0) | 2024.12.28 |
[Spring AOP] 프록시 팩토리 (0) | 2024.12.28 |
[Spring AOP] 동적 프록시 (0) | 2024.12.28 |
[Spring] ThreadLocal (0) | 2024.12.28 |