스프링 부트 - 포인트컷, 어드바이스, 어드바이저

SeungTaek·2021년 11월 2일
0
post-thumbnail

본 게시물은 스스로의 공부를 위한 글입니다.
잘못된 내용이 있으면 댓글로 알려주세요!


프록시, JDK 동적 프록시에 대해 알아보기

CGLIB, ProxyFactory에 대해 알아보기


📒 용어 정리

  • 포인트컷(Pointcut): 부가 기능을 적용할지, 안할지 판단하는 필터링 로직. 주로 클래스와 메서드 이름으로 구분
  • 어드바이스(Advice): 프록시가 호출하는 부가 기능(프록시 로직)
  • 어드바이저(Advisor): 포인트컷1 + 어드바이스1

📌 포인트컷

  • 스프링에서는 포인트컷을 쉽게 사용하도록 무수히 많은 포인트컷을 제공해준다.
    • NameMatchMethodPointcut
      • .setMappedNames( {메서드 이름} )을 사용하면 해당하는 메서드만 부가 기능이 실행된다.
      • 내부에서 PatternMatchUtils를 사용하므로 *XXX* 형식 허용
        • ex) pointcut.setMappedNames("request*", "order*", "save*");
  • JdkRegexpMethodPointcut(): JDK 정규 표현식을 기반으로 포인트컷을 매칭한다.
  • Pointcut.TRUE: 항상 참을 반환한다.
  • AnnotationMatchingPointcut: 애노테이션으로 매칭한다.
  • AspectJExpressionPointcut: aspectJ 표현식으로 매칭한다.
    • 실무에서는 결국 이걸 사용한다.
    • 이 방식은 나중에 AOP 게시물에서 다루겠다.


📌 어드바이저

  • new DefaultPointcutAdvisor( {포인트컷}, {어드바이스} ): 어드바이저의 가장 일반적인 구현체이다. 하나의 포인터컷과 하나의 어드바이스를 넣어주면 된다.
  • 프록시팩토리에 어드바이저를 지정하려면 .addAdvisor( {어드바이저} )를 사용하면 된다.
    • 만일 .addAdvisor( {어드바이스} )만 지정해준다면, 자동으로 포인트컷은 Pointcut.TRUE가 들어간다.


📌 어드바이스

  • MethodInterceptorinvoke를 구현하면 된다.
  • 자세한 내용은 여기



📒 예제

  • 🎈 어드바이스 생성
@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={}", resultTime);
        return result;
    }
}

  • 🎈 ServiceInterface와 구현 클래스 생성
public interface ServiceInterface {
    void save();
    void find();
}
@Slf4j
public class ServiceImpl implements ServiceInterface {
    @Override
    public void save() {
      log.info("save 호출");
    }

    @Override
    public void find() {
        log.info("find 호출");
    }
}

📌 확인

@Test
void advisorTest3() {
    //target과 프록시 팩토리 생성
    ServiceInterface target = new ServiceImpl();
    ProxyFactory proxyFactory = new ProxyFactory(target);
    
    //스프링이 기본 제공하는 pointcut
    //setMappedNames( {매칭 메소드 이름})
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.setMappedNames("save");
    
    //어드바이저는 하나의 pointcut과 하나의 advice로 이루어져있다.
    //프록시 팩토리에 .addAdvisor로 넣어주기
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice());
    proxyFactory.addAdvisor(advisor);
    
    ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

    proxy.save();
    proxy.find();
}


//출력확인
TimeAdvice - TimeProxy 실행
ServiceImpl - save 호출
TimeAdvice - TimeProxy 종료 resultTime=0
ServiceImpl - find 호출
  • save()find()를 실행했지만, .setMappedNames("save")에 지정한것 처럼 save() 메서드만 프록시의 부가 기능이 실행됐다.





📌 멀티 어드바이스

하나의 프록시에 여러 어드바이스를 적용하려면 다음과 같이 한다

  • TimeAdvice1, TimeAdvice2 생성은 생략
@Test
void multiAdvisorTest2() {
    //client -> proxy -> TimeAdvice2 -> TimeAdvice1 -> target

    DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice1());
    DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice2());


    //프록시 생성
    ServiceInterface target = new ServiceImpl();
    ProxyFactory proxyFactory = new ProxyFactory(target);

    proxyFactory.addAdvisor(advisor2);
    proxyFactory.addAdvisor(advisor1);
    ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

    proxy.save();
}
  • 너무나 간단하게.. 프록시 팩토리에 .addAdvisor()을 여러번 사용해서 어드바이저를 넣어주면 끝난다.
  • 이때 어드바이저를 넣어준 순서대로 실행된다.

인프런의 '스프링 핵심 원리 고급편(김영한)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.

profile
I Think So!

0개의 댓글