[Spring] Pointcut, Advise, Advisor κΈ°λ³Έ κ°œλ…μ„ μ•Œμ•„λ³΄μž

2022. 6. 3. 13:54γ†πŸŒΏ Spring

  • 포인트컷 (pointcut) : 필터링 ν•  μœ„μΉ˜λ₯Ό μ§€μ •ν•˜λŠ” 필터링 λ‘œμ§μ΄λ‹€. 즉 어디에 λΆ€κ°€ κΈ°λŠ₯을 μ μš©ν• μ§€ μ΄ˆμ μ„ λ‘”λ‹€. 
  • μ–΄λ“œλ°”μ΄λ“œ (advice) : ν”„λ‘μ‹œκ°€ ν˜ΈμΆœν•˜λŠ” λΆ€κ°€ κΈ°λŠ₯ 이닀. λ‹¨μˆœνžˆ ν”„λ‘μ‹œκ°€ μ μš©ν•  λΆ€κ°€κΈ°λŠ₯ 둜직이라 μƒκ°ν•˜λ©΄ λœλ‹€. 
  • μ–΄λ“œλ°”μ΄μ € (advisor) : λ‹¨μˆœν•˜κ²Œ ν•˜λ‚˜μ˜ 포인트컷과 ν•˜λ‚˜μ˜ μ–΄λ“œλ°”μ΄μŠ€λ₯Ό 가지고 μžˆλŠ” 것이닀. μ‰½κ²Œ 생각해 포인트컷 1 : μ–΄λ“œλ°”μ΄μŠ€ 1  인 것.

λ¬Έμž₯으둜 μ •λ¦¬ν•˜λ©΄, λΆ€κ°€ κΈ°λŠ₯ λ‘œμ§μ„ μ μš©ν•  λ•Œ , 어디에 μ μš©ν• μ§€ μœ„μΉ˜ 지정은 pointcut 이 λ‹΄λ‹Ήν•˜κ³ , μ–΄λ–€ λ‘œμ§μ„ μ μš©ν• μ§€λŠ” advice κ°€ λ‹΄λ‹Ήν•œλ‹€. κ·Έλ¦¬ν•˜μ—¬ 어디에, μ–΄λ–€ λΆ€κ°€ κΈ°λŠ₯ λ‘œμ§μ„ 지정할지 λͺ¨λ‘ μ•Œκ³  μžˆλŠ” 것이 μ–΄λ“œλ°”μ΄μ €(advisor)κ°€ λœλ‹€.

 

 

μ—­ν• κ³Ό μ±…μž„

  • advice, pointcut κ°œλ…μ΄ 없을 λ•ŒλŠ”, proxy λΆ€κ°€ κΈ°λŠ₯ 둜직 λ‚΄λΆ€μ—μ„œ if 문으둜 λΆ„κΈ°ν•˜μ—¬ Pattern 맀칭으둜 필터링 λ‘œμ§μ„ κ΅¬ν˜„ν–ˆλ‹€. μ΄λŠ” SRP λ₯Ό μœ„λ°˜ν•˜λ©° μ½”λ“œλ₯Ό κΉ”λ”ν•˜κ²Œ λ‚˜νƒ€λ‚΄μ§€λ„ λͺ»ν•œλ‹€.
  • ν¬μΈνŠΈμ»·μ€ λŒ€μƒ μ—¬λΆ€λ₯Ό ν™•μΈν•˜λŠ” ν•„ν„° μ—­ν• λ§Œ λ‹΄λ‹Ήν•˜κ²Œ 되고, 
  • μ–΄λ“œλ°”μ΄μŠ€λŠ” λΆ€κ°€ κΈ°λŠ₯ 둜직만 λ‹΄λ‹Ήν•˜κ²Œ λœλ‹€. 
  • λ‘˜μ„ ν•©μΉ˜λ©΄ μ–΄λ“œλ°”μ΄μ €κ°€ λœλ‹€. 즉 μŠ€ν”„λ§ Advisor λŠ” ν•˜λ‚˜μ˜ pointcut + ν•˜λ‚˜μ˜ advice 둜 κ΅¬μ„±λœλ‹€. 
  • μ—¬κΈ°μ„œ μ€‘μš”ν•œ 점은 ν•˜λ‚˜μ˜ μ–΄λ“œλ°”μ΄μŠ€μ— μ—¬λŸ¬ λΆ€κ°€κΈ°λŠ₯ λ‘œμ§μ„ μΆ”κ°€ν•  수 μžˆλ‹€λŠ” 점이닀. 이 뢀뢄을 μ•„λž˜μ—μ„œ 더 μ•Œμ•„λ³΄μž. 

 

 

 

쀑간 정리

μš°λ¦¬κ°€ advice, pointcut, advisor 같은 κ°œλ…μ„ λ°°μš°λŠ” μ΄μœ λŠ” ν”„λ‘μ‹œλ₯Ό μ μš©ν•˜κΈ° μœ„ν•¨μ΄λ‹€.

그럼 ν”„λ‘μ‹œλŠ” μ™œ λ°°μš°λŠ”κ°€? ν΄λΌμ΄μ–ΈνŠΈκ°€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ μ‹œ μ˜λ„λŒ€λ‘œ μ‚¬μš©λ  κΈ°λŠ₯은 save() 와 find() 이닀 (μœ„ κ·Έλ¦Ό μ°Έκ³ ).

이 λ•Œ save(), find() λŠ” μ •μƒμ μœΌλ‘œ μ‹€ν–‰μ‹œν‚€λ˜, κΈ°νšμ˜λ„λ‚˜ 개발 μ΄μŠˆμƒ λΆ€κ°€μ μœΌλ‘œ μ‹€ν–‰ν•΄μ•Ό ν•  κΈ°λŠ₯이 μ‘΄μž¬ν•  수 μžˆλ‹€. 그럴 경우 ν”„λ‘μ‹œλ₯Ό μ‚½μž…ν•˜μ—¬, target μΈμŠ€ν„΄μŠ€μ˜ κΈ°λŠ₯은 μ •μƒμ μœΌλ‘œ μ‹€ν–‰μ‹œν‚€κ³ , μ€‘κ°„μ—μ„œ λΆ€κ°€μ μœΌλ‘œ μ‹€ν–‰ν•˜κ³  싢은 κΈ°λŠ₯도 μΆ”κ°€ν•  수 μžˆλ‹€. 이것이 ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜λŠ” μ˜λ„ 쀑 ν•˜λ‚˜λΌ 짚고 λ„˜μ–΄κ°€λ©΄ μ’‹κ² λ‹€.

 

 

 

μ–΄λ“œλ°”μ΄μ € - 예제 μ½”λ“œ

@Slf4j
public class AdvisorTest {

    @Test
    void advisorTest1() {
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);

        // 이전과 달라진 λΆ€λΆ„ : DefaultPointcutAdvisor() 객체 생성.
        // Advisor μΆ”κ°€ -> ν•˜λ‚˜μ˜ pointcut κ³Ό ν•˜λ‚˜μ˜ advice λ₯Ό κ°–λŠ”λ‹€.
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice());

        proxyFactory.addAdvisor(advisor); // κΈ°μ‘΄ .addAdvise() -> .addAdvisor()
        // addAdvise λ‚΄λΆ€λ₯Ό 보면 νŽΈμ˜μƒ addAdvisor 도 적용이 λ˜μ–΄μžˆλ‹€.

        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

        proxy.save();
        proxy.find();
    }
}
  • new DefaulPointcutAdvisor : Advisor μΈν„°νŽ˜μ΄μŠ€ κ°€μž₯ 일반적인 κ΅¬ν˜„μ²΄. μƒμ„±μžλ₯Ό 톡해 ν•˜λ‚˜μ˜ 포인트컷과 ν•˜λ‚˜μ˜ μ–΄λ“œλ°”μ΄μŠ€λ₯Ό λ„£μ–΄μ£Όλ©΄ λœλ‹€. 첫번째 μΈμžμ—λŠ” pointcut, λ‘λ²ˆμ§Έ μΈμžμ—λŠ” adviceκ°€ λ“€μ–΄κ°€λ©΄ λœλ‹€.
  • Pointcut.TRUE : 항상 true λ₯Ό λ°˜ν™˜ν•˜λŠ” 포인트컷으둜, μ–΄λ– ν•œ 필터도 μ—†μŒμ„ μ˜λ―Έν•œλ‹€.
  • proxyFactory.addAdvisor(advisor) : ν”„λ‘μ‹œ νŒ©ν† λ¦¬μ— μ μš©ν•  μ–΄λ“œλ°”μ΄μ €λ₯Ό μ§€μ •ν•œλ‹€. μ–΄λ“œλ°”μ΄μ €λŠ” 내뢀에 포인트컷과 μ–΄λ“œλ°”μ΄μŠ€λ₯Ό λͺ¨λ‘ 가지고 있기 λ•Œλ¬Έμ—, 어디에 μ–΄λ–€ λΆ€κ°€κΈ°λŠ₯을 μ μš©ν•΄μ•Όν• μ§€ μ–΄λ“œλ°”μ΄μ € ν•˜λ‚˜λ‘œ μ•Œ 수 μžˆλ‹€.

 

 

직접 λ§Œλ“  포인트컷 - 예제 μ½”λ“œ 

ν¬μΈνŠΈμ»·μ„ μ»€μŠ€ν…€ν•˜μ—¬ 직접 생성할 수 μžˆλ‹€ .λ¬Όλ‘  μŠ€ν”„λ§μ—μ„œ μ œκ³΅ν•˜λŠ” κ°„λ‹¨ν•œ 방법도 μ‘΄μž¬ν•œλ‹€. ν•˜μ§€λ§Œ 직접 포인트 컷을 μƒμ„±ν•˜λ©΄ μ’€ 더 μœ μ—°ν•˜κ²Œ 둜직 처리λ₯Ό ν•˜κ±°λ‚˜ 둜그λ₯Ό 남길 수 μžˆλŠ” 이점이 μžˆλ‹€.

 

λ‚΄λΆ€ 정적 클래슀둜 MyPointcut μ΄λΌλŠ” 클래슀λ₯Ό μƒμ„±ν•˜κ³  Pointcut μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œλ‹€. 

methodMatcher 좔상 λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν•˜μ—¬, ν¬μΈνŠΈμ»·μ„ μ μš©ν•  λ‘œμ§μ„ μ‚½μž…ν•  수 μžˆλ‹€.

μ•„λž˜ μ½”λ“œλŠ” "save" λΌλŠ” 이름을 가진 λ©”μ„œλ“œμ— ν”„λ‘μ‹œλ₯Ό μ μš©ν•˜λ„λ‘ matchName = "save" 둜 μ§€μ •ν•˜μ˜€λ‹€.

@Slf4j
public class AdvisorTest {

    @Test
    @DisplayName("직접 λ§Œλ“  포인트컷")
    void advisorTest2() {
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);

        // κΈ°μ‘΄ Pointcut.TRUE -> new MyPointcut()
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(new MyPointcut(), new TimeAdvice());
        proxyFactory.addAdvisor(advisor);
        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

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

    static class MyPointcut implements Pointcut {

        @Override
        public ClassFilter getClassFilter() {
            return ClassFilter.TRUE;
        } // 항상 TRUE λ°˜ν™˜

        @Override
        public MethodMatcher getMethodMatcher() {
            return new MyMethodMatcher();
        }
    }

    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 λ₯Ό μƒμ†ν•œ MyMethodMatcher ν΄λž˜μŠ€λŠ” 2개의 matches 와  isRuntime λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν•œλ‹€. 이 λ•Œ isRuntime이 false λ₯Ό λ¦¬ν„΄ν•˜λ©΄ 첫번째 matches κ°€ 호좜되고, true 일 땐  μ•„λž˜μ˜ matches(... args) κ°€ ν˜ΈμΆœλœλ‹€. 즉 λ™μ μœΌλ‘œ λ„˜μ–΄μ˜€λŠ” λ§€κ°œλ³€μˆ˜λ₯Ό νŒλ‹¨ν•˜λŠ” 둜직으둜 μ‚¬μš©ν•  수 μžˆλ‹€. 

isRuntime() 이 false 인 경우, 클래슀의 정적 μ •λ³΄λ§Œ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— (runtime = false) , μŠ€ν”„λ§μ΄ λ‚΄λΆ€μ—μ„œ 캐싱을 톡해 μ„±λŠ₯ ν–₯상이 κ°€λŠ₯ν•˜μ§€λ§Œ, isRuntime() 이 true 인 경우 λ§€κ°œλ³€μˆ˜κ°€ λ™μ μœΌλ‘œ λ³€κ²½λœλ‹€κ³  κ°€μ •ν•˜κΈ° λ•Œλ¬Έμ— 캐싱을 ν•˜μ§€ μ•ŠλŠ”λ‹€. 

 

κ²°κ³Ό 

μ‹€ν–‰ κ²°κ³Όλ₯Ό ν™•μΈν•˜λ©΄, save() λ₯Ό ν˜ΈμΆœν•  땐 TimeProxy κ°€ 잘 적용되고, find() λ₯Ό ν˜ΈμΆœν•  땐 μ μš©λ˜μ§€ μ•Šμ€κ²ƒμ„ 확인할 수 μžˆλ‹€. 

 

 

 

μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” 포인트컷 - 예제 μ½”λ“œ 

μœ„μ—μ„œλŠ” μš°λ¦¬κ°€ 직접 MethodMatcher λ₯Ό 상속받아 ν¬μΈνŠΈμ»·μ„ κ°œλ°œν–ˆμ§€λ§Œ, μŠ€ν”„λ§μ€ μš°λ¦¬κ°€ ν•„μš”ν•œ ν¬μΈνŠΈμ»·μ„ 이미 λŒ€λΆ€λΆ„ μ œκ³΅ν•œλ‹€. 

pointcut.setMappedNames("save") 처럼 ν•œμ€„μ˜ μ½”λ“œλ‘œ ν¬μΈνŠΈμ»·μ„ 지정할 수 μžˆλ‹€. 이처럼 NameMatchMethodPointcut 의 μΈμŠ€ν„΄μŠ€λ‘œ setMappedNames() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ κ°„λ‹¨ν•˜κ²Œ λ©”μ„œλ“œ 이름을 μ΄μš©ν•œ ν¬μΈνŠΈμ»·μ„ λ§Œλ“€ 수 μžˆλ‹€. 

@Slf4j
public class AdvisorTest {

    @Test
    @DisplayName("μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” 포인트컷")
    void advisorTest3() {
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);

        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.setMappedNames("save"); // λ©”μ„œλ“œ 이름이 save 인 κ²½μš°μ—λ§Œ κ°€λŠ₯ν•˜λ„λ‘.

        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice());
        proxyFactory.addAdvisor(advisor);
        ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();

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

}
  • NameMatchMethodPointcut : λ©”μ„œλ“œ 이름을 기반으둜 λ§€μΉ­ν•œλ‹€. λ‚΄λΆ€μ—μ„œλŠ” PatternMatchUtils λ₯Ό μ‚¬μš©ν•œλ‹€. 예) *xxx* ν—ˆμš©
  • JdkRegexpMethodPointcut : JDK μ •κ·œ ν‘œν˜„μ‹μ„ 기반으둜 ν¬μΈνŠΈμ»·μ„ λ§€μΉ­ν•œλ‹€.
  • TruePointcut : 항상 참을 λ°˜ν™˜ν•œλ‹€.
  • AnnotationMatchingPointcut : μ• λ…Έν…Œμ΄μ…˜μœΌλ‘œ λ§€μΉ­ν•œλ‹€.
  • AspectJExpressionPointcut : aspectJ ν‘œν˜„μ‹μœΌλ‘œ λ§€μΉ­ν•œλ‹€. 

κ°€μž₯ μ€‘μš”ν•œ 것은 aspectJ ν‘œν˜„μ‹
μœ„μ˜ μ—¬λŸ¬ pointcut μ˜΅μ…˜ λ³΄λ‹€λŠ” aspectJ λ₯Ό μ‹€λ¬΄μ—μ„œ κ°€μž₯ 많이 μ‚¬μš©ν•œλ‹€. νŽΈλ¦¬ν•˜κ³  κΈ°λŠ₯도 κ°€μž₯ λ§Žμ€ aspectJ ν‘œν˜„μ‹μ„ 기반으둜 μ‚¬μš©ν•˜λŠ” AspectJExpressionPointcut 을 주둜 μ‚¬μš©ν•˜κ²Œ 될 것이닀. aspectJ ν‘œν˜„μ‹κ³Ό μ‚¬μš© 방법은 μ€‘μš”ν•΄μ„œ 이후 AOPλ₯Ό μ„€λͺ…ν•  λ•Œ μžμ„Ένžˆ μ„€λͺ…ν•˜κ² λ‹€.

 

 

μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μ €μ™€ ν•¨κ»˜ μ‚¬μš©

μ–΄λ“œλ°”μ΄μ €λŠ” ν•˜λ‚˜μ˜ 포인트컷과 ν•˜λ‚˜μ˜ μ–΄λ“œλ°”μ΄μŠ€λ₯Ό 가진닀고 ν–ˆλŠ”λ°, μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μŠ€λ₯Ό ν•˜λ‚˜μ˜ target 에 μ μš©ν•˜λŠ” κ²½μš°λŠ” κ°€λŠ₯ν• κΉŒ? ν”„λ‘μ‹œλ₯Ό μ—¬λŸ¬κ°œ λ§Œλ“€μ–΄ chaining ν•˜λŠ” 방법을 μ μš©ν•΄λ³Ό 수 μžˆλ‹€.

 

κΈ°μ‘΄


public class Before_MultiAdvisorTest {

    @Test
    @DisplayName("μ—¬λŸ¬κ°œμ˜ ν”„λ‘μ‹œ μ‚¬μš©")
    void multiAdvisorTest1() {
//        client -> proxy2(advisor2) -> proxy1(advisor1) -> target

//        ν”„λ‘μ‹œ1 생성
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory1 = new ProxyFactory(target);
        DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
        proxyFactory1.addAdvisor(advisor1);
        ServiceInterface proxy1 = (ServiceInterface) proxyFactory1.getProxy();

        //ν”„λ‘μ‹œ2 생성, target -> proxy1 μž…λ ₯
        ProxyFactory proxyFactory2 = new ProxyFactory(proxy1); // target 이 μ•„λ‹Œ proxy1 을 λ„£μ—ˆλ‹€.
        DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
        proxyFactory2.addAdvisor(advisor2);
        ServiceInterface proxy2 = (ServiceInterface) proxyFactory2.getProxy();

        //μ‹€ν–‰
        proxy2.save();
    }

    @Slf4j
    static class Advice1 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("advice1 호좜");
            return invocation.proceed();
        }
    }

    @Slf4j
    static class Advice2 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("advice2 호좜");
            return invocation.proceed();
        }
    }

}

 

λ³€κ²½ ν›„ : ν•˜λ‚˜μ˜ ν”„λ‘μ‹œμ— μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μ € μ‚¬μš©

μœ„ 방법은 proxyFactory λ₯Ό μ—¬λŸ¬κ°œ μƒμ„±ν•˜λŠ” λ¬Έμ œκ°€ μžˆλ‹€. 사싀 잘λͺ»λœ 것은 μ•„λ‹ˆμ§€λ§Œ, ꡳ이 ν”„λ‘μ‹œ νŒ©ν† λ¦¬ 객체λ₯Ό μ—¬λŸ¬κ°œ 생성할 ν•„μš”κ°€ μ—†λ‹€λŠ” 점을 인지해야 ν•œλ‹€. μ–΄λ“œλ°”μ΄μŠ€κ°€ λ§Žμ•„μ§ˆ 수둝, 그만큼 ν”„λ‘μ‹œ νŒ©ν† λ¦¬λ„ λŠ˜μ–΄λ‚˜κΈ° λ•Œλ¬Έμ΄λ‹€.

 

μ•„λž˜ 방법은 ν”„λ‘μ‹œ νŒ©ν† λ¦¬ ν•˜λ‚˜μ— μ—¬λŸ¬κ°œμ˜ μ–΄λ“œλ°”μ΄μ €λ₯Ό μ μš©ν•œ ν˜•νƒœλ‹€.

public class MultiAdvisorTest {

    @Test
    @DisplayName("ν•˜λ‚˜μ˜ ν”„λ‘μ‹œ, μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μ €")
    void multiAdvisorTest2() {
        // κΈ°μ‘΄
        // client -> proxy2(advisor2) -> proxy1(advisor1) -> target
        // λ³€κ²½ ν›„
        // client -> proxy -> advisor2 -> advisor1 -> target

        DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
        DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());

        //ν”„λ‘μ‹œ1 생성
        ServiceInterface target = new ServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(target);

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

        //μ‹€ν–‰
        proxy.save();
        // ν•˜λ‚˜μ˜ ν”„λ‘μ‹œμ—μ„œ μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μ €λ₯Ό 등둝할 수 μžˆλ‹€.
        // advisor2μ—μ„œ λ¨Όμ € 포인트컷 μ μš©ν•˜κ³ , κ·Έλ‹€μŒμ— advisor1μ—μ„œ 포인트컷 μ μš©ν•œλ‹€.
        // 결과적으둜 μ—¬λŸ¬ ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•  λ•Œμ™€ λΉ„κ΅ν•˜λ©΄, κ²°κ³ΌλŠ” κ°™κ³  μ„±λŠ₯은 더 μ’‹λ‹€. (ν•˜λ‚˜μ˜ ν”„λ‘μ‹œλ§Œ μ‚¬μš©ν•œ 것)

    }

    @Slf4j
    static class Advice1 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("advice1 호좜");
            return invocation.proceed();
        }
    }

    @Slf4j
    static class Advice2 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            log.info("advice2 호좜");
            return invocation.proceed();
        }
    }

}

 

ν•˜λ‚˜μ˜ proxyFactory 에 addAdvisor() λ₯Ό λ‘λ²ˆ ν˜ΈμΆœν•΄ μ„œλ‘œ λ‹€λ₯Έ advisor λ₯Ό μ μš©ν•œ 것을 확인할 수 μžˆλ‹€. μ€‘μš”ν•œ 점은 λ“±λ‘ν•˜λŠ” μˆœμ„œλŒ€λ‘œ advisor κ°€ 호좜되기 λ•Œλ¬Έμ—, 등둝 μˆœμ„œλ„ μœ λ…ν•˜μ—¬ κ°œλ°œν•΄μ•Όν•œλ‹€.

 

ν”„λ‘μ‹œ κ°μ²΄λŠ” ν•˜λ‚˜λ§Œ μƒμ„±λœλ‹€

λ§Žμ€ κ°œλ°œμžλ“€μ΄ 착각할 수 μžˆλŠ” 지점이, AOP  적용 수 만큼 ν”„λ‘μ‹œκ°€ μƒμ„±λœλ‹€κ³  μ˜€ν•΄ν•œλ‹€. μŠ€ν”„λ§μ€ AOP λ₯Ό μ μš©ν•  λ•Œ, μ΅œμ ν™”λ₯Ό μ§„ν–‰ν•˜μ—¬ μ§€κΈˆμ²˜λŸΌ ν”„λ‘μ‹œλ₯Ό ν•˜λ‚˜λ§Œ λ§Œλ“€κ³ , ν•˜λ‚˜μ˜ ν”„λ‘μ‹œμ— μ—¬λŸ¬ μ–΄λ“œλ°”μ΄μ €λ₯Ό μ μš©ν•œλ‹€. μ •λ¦¬ν•˜λ©΄  ν•˜λ‚˜μ˜ target 에 μ—¬λŸ¬ AOP κ°€ λ™μ‹œμ— μ μš©λ˜μ–΄λ„, μŠ€ν”„λ§μ˜ AOP λŠ” ν•˜λ‚˜μ˜ ν”„λ‘μ‹œλ§Œ μƒμ„±ν•œλ‹€.