티스토리 뷰
포인트컷
포인트컷은 어디에 부가 기능을 적용할지 판단하는 필터링 로직으로, 주로 클래스와 메서드 이름으로 필터링한다. 어떤 포인트(Point)에 기능을 적용할지 하지 않을지 잘라서(cut) 구분하는 것이라 이해하면 된다.
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
public interface ClassFilter {
boolean matches(Class<?> clazz);
}
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
//..
}
포인트컷은 크게 ClassFilter
와 MethodMatcher
둘로 이루어진다.ClassFilter
는 클래스가 맞는지, MethodMatcher
는 메서드가 맞는지 확인할 때 사용하며, 둘다 true
로 반환해야 어드바이스를 적용할 수 있다.
포인트컷은 대상 여부를 확인하는 필터 역할만 수행하고, 어드바이스는 부가 기능 로직만 수행함으로써, 역할과 책임이 명확하게 분리된 것을 확인할 수 있다. 스프링의 어드바이저는 하나의 포인트컷과 하나의 어드바이스로 구성된다.
포인트컷(Pointcut) 만들어보기
포인트컷을 이용해서 save()
메서드에는 어드바이스 로직을 적용하지만, find()
메서드에는 어드바이스 로직을 적용하지 않도록 해보자.
MyPointcut.class
static class MyPointcut implements Pointcut {
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
@Override
public MethodMatcher getMethodMatcher() {
return new MyMethodMatcher();
}
}
Pointcut
인터페이스를 직접 구현한 MyPointcut
이다.
클래스 필터는 항상 true
를 반환하도록 했고, 메서드 비교 기능은 MyMethodMatcher
를 사용한다.
MyMethodMatcher.class
static class MyMethodMatcher implements MethodMatcher {
private String matchName = "save";
@Override
public boolean matches(Method method, Class<?> targetClass) {
boolean result = method.getName().equals(matchName);
log.info("포인트컷 호출 method={} targetClass={}", method.getName(), targetClass);
log.info("포인트컷 결과 result={}", result);
return result;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
}
MethodMatcher
인터페이스를 직접 구현한 MethodMatcher
이다.
matches()
에는 method
, targetClass
정보가 넘어오는데, 이 정보로 어드바이스를 적용할지 적용하지 않을지 판단할 수 있다.
save
라는 이름을 가진 메서드만 어드바이스가 적용되도록 설정하였다. MyPointcut
을 이용한다면, save
이외에는 어드바이스가 적용되지 않아야 할 것이다.
Test code
@DisplayName("직접 만든 포인트컷")
@Test
void madePointcut() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(new MyPointcut(), new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save(); // 어드바이스가 적용된다.
proxy.find(); // 어드바이스가 적용되지 않는다.
}
결과
실행된 결과를 보면, 기대한 것과 같이 save()
를 호출할 때는 어드바이스가 적용됐지만, find()
를 호출할 때는 어드바이스가 적용되지 않은 모습을 확인할 수 있다.
Reference
김영한. 스프링 핵심 원리 - 고급편. 인프런.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8#
'Spring' 카테고리의 다른 글
[Spring] 메시지 컨버터(Message Converter) (0) | 2022.05.21 |
---|---|
[Spring] 빈 후처리기(BeanPostProcessor) (0) | 2022.05.21 |
[Spring] Advice 만들어보기 (0) | 2022.05.17 |
[Spring] 리플렉션(Reflection) (0) | 2022.05.15 |
[Spring] ThreadLocal을 사용해보자! (0) | 2022.05.07 |
- Total
- Today
- Yesterday
- 노마드
- 인프런
- 코틀린
- 구현
- 데이터베이스
- 스프링 부트
- mysql 8.0
- 스프링
- 백준
- 코테
- kotlin
- 자료구조
- Algorithm
- Spring
- 파이썬
- 알고리즘
- spring boot
- 김영한
- 정렬
- 스프링부트
- MySQL
- 노마드코더
- 문자열
- webflux
- leetcode
- 리팩토링
- Real MySQL
- 북클럽
- 그리디
- 릿코드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |