728x90
반응형
728x90
ProxyFactory
- 스프링 프레임워크에서 프록시 객체를 생성하는 데 사용되는 유틸리티 클래스
- 두 가지 종류의 프록시 생성 방법을 추상화하여 제공
- JDK 프록시: 인터페이스 기반
- CGLIB 프록시: 클래스 기반
구성 요소
target
- 프록시가 감쌀 실제 객체
- ProxyFactory 생성 시 생성자로 넘기거나 setTarget을 통해 지정 가능
//생성자
ProxyFactory proxyFactory = new ProxyFactory(new HelloImpl());
//setTarget
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new HelloImpl());
Advice
- 프록시가 메서드 호출 시 실행할 로직
- 실행 전 후 등 추가 로직이 구현되는 곳
//Advice - 실제 객체 호출 전 후 프록시에서 추가 구현 로직이 들어가는 곳
public class LogInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 메서드 호출 전 로깅
log.info("Before method={}", invocation.getMethod().getName());
// 실제 메서드 호출
Object result = invocation.proceed();
// 메서드 호출 후 로깅
System.out.println("After method={}", invocation.getMethod().getName());
return result;
}
}
//Advice 지정
proxyFactory.addAdvice(new LogInterceptor());
Pointcut
- 메서드의 특정 지점에서 Advice가 실행될지를 결정하는 표현식
- 프록시에서 추가 로직(Advice)을 실행 여부를 검사하는 클래스
- AspectJExpressionPointcut 클래스를 사용하던가 Pointcut을 상속받아 구현하여 사용
public class CustomPointcut implements Pointcut {
//MethodMatcher 구현
public class CustomMethodMatcher implements MethodMatcher {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 메서드 이름이 login* 또는 register*로 시작하는지 확인
String methodName = method.getName();
return methodName.startsWith("login") || methodName.startsWith("register");
}
@Override
public boolean isRuntime() {
return false; // 런타임에서 결정되지 않음, 정적 판단
}
}
//Pointcut 구현
@Override
public MethodMatcher getMethodMatcher() {
return new CustomMethodMatcher();
}
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE; // 모든 클래스를 허용
}
}
//AspectJExpressionPointcut
public class AspectJPointcut {
public AspectJExpressionPointcut getPointcut() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// AspectJ 표현식으로 login* 또는 register*로 시작하는 메서드 설정
pointcut.setExpression("execution(void login*(..)) || execution(void register*(..))");
return pointcut;
}
}
Advisor
- Pointcut과 Advice를 1개 씩 가지고 있는 클래스
- 해당 Pointcut을 만족하면 Advice 실행
- 한 번에 여러 Advisor 적용 가능
Pointcut pointcut1 = new AspectJPointcut().getPointcut(); //AspectJPointcut 방식
Pointcut pointcut2 = new CustomPointcut(); //Pointcut 구현
LogInterceptor advice = new LogInterceptor(); // Advice
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(pointcut1, advice); //Advisor1
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(pointcut2, advice); //Advisor2
ProxyFactory proxyFactory = new ProxyFactory(new HelloImpl());
proxyFactory.addAdvisor(advisor2); //Advisor2 적용
proxyFactory.addAdvisor(advisor1); //Advisor1 적용
//요청 흐름
client -> proxy -> advisor2 -> advisor1 -> target
사용 방법
- target 생성
- 프록시 객체로 감쌀 실제 객체 생성
- ProxyFactory 생성
- ProxyFatory 객체 생성과 프록시가 참조할 실제 target 객체 지정
- Advisor 생성 및 적용
- Advice와 Pointcut 생성 후 Advisor 생성
- ProxyFactory에 Advisor 지정
- 추가 설정
- setProxyTargetClass(): CGLIB 프록시 사용 여부 설정 -> 인터페이스 및 실제 클래스가 존재하는 경우
- setInterfaces(): 프록시가 구현할 인터페이스 지정 -> 실제 클래스가 구현한 인터페이스와 관계없이 프록시 객체가 다른 인터페이스를 구현 가능
- setTargetSource(): target 객체 제공 방식 설정
- 프록시 객체 생성
- proxyFactory.getProxy()를 통해 프록시 객체 생성
장점
- 유연한 적용 가능 -> 인터페이스만 존재하거나 클래스만 존재하는 모든 경우에 사용 가능
- Pointcut과 Advice의 역할을 나누어 단일 책임 준수
스프링 핵심 원리 - 고급편 강의 | 김영한 - 인프런 강의 내용 참고
728x90
'백엔드 > Spring' 카테고리의 다른 글
[Spring] AOP - Aspect-Oriented Programming 개념 (0) | 2024.11.08 |
---|---|
[Spring] 빈 후처리기 - BeanPostProcessor (0) | 2024.11.07 |
[Spring] 동적 프록시 (0) | 2024.11.05 |
[Spring] Proxy & Decorator Pattern (0) | 2024.11.02 |
[Spring] Strategy & Template Callback (0) | 2024.11.01 |