Java & Spring/토비의 스프링 3.1

6장) 6.5 스프링 AOP

Zin0_0 2021. 7. 23. 15:52
반응형

6장 AOP

  • 부가기능 적용 후에 기존 설계 코드에 영향을 주지 않도록 제공돼야 함
    • 다른 코드에서는 존재가 보이지 않지만, 메소드가 호출되는 과정에서 다이내믹하게 부가적인 기능을 제공

6.5.1 자동 프록시 생성

  • 타깃 오브젝트마다 비슷한 내용의 ProxyFactoryBean 설정 정보 추가 부분이 남은 해결 과제
  • 중복 문제의 접근 방법
    • JDBC API를 사용하는 DAO 코드
      • 전략 패턴과 DI를 적용해서 템플릿과 콜백, 클라이언트로 나누어 해결
    • 반복적인 위임 코드가 필요한 프록시 클래스 코드
      • 다이내믹 프록시와 다이내믹 프록시 생성 팩토리 DI를 사용해서 런타임 코드 자동생성 기법으로 해결
    • 반복적인 ProxyFactoryBean 설정
      • 미해결
  • 빈 후처리기를 이용한 자동 프록시 생성기
    • 스프링은 컨테이너로서 제공하는 기능 중 변하지 않는 핵심 부분 외에는 대부분 확장할 수 있는 확장 포인트를 제공
      • BeanPostProcessor 인터페이스를 구현해서 만드는 빈 후처리기 존재
      • DefaultAdvisorAutoProxyCreator 빈 후처리기 이용
        • 어드바이저를 이용한 자동 프록시 생성기
        • 빈 후처리기 자체를 빈으로 등록하여 스프링에 적용
          • 빈 오브젝트가 생성될 때마다 빈 후처리기에 보내서 후처리 작업 요청
          • 프로퍼티 강제 수행 및 초기화 작업 수행, 만들어진 빈 오브젝트 자체를 바꿀 수 있음
            • 스프링이 설정을 참고해서 만든 오브젝트가 아닌 다른 오브젝트를 빈으로 등록 가능
        • 빈 오브젝트의 일부를 프록시로 포장하고 프록시를 빈으로 등록 ~> 자동 프록시 생성 빈 후처리기
      • DefaultAdvisorAutoProxyCreator 빈 후처리기 등록
        ~> 빈 오브젝트를 만들 때마다 후처리기에 빈을 보냄
        ~> 빈으로 등록된 모든 어드바이저 내의 포인트컷으로 프록시 적용 대상인지 확인
        ~> 내장된 프록시 생성기에게 현재 빈에 대한 프록시를 만들게 하고, 어드바이저를 연결
        ~> 프록시가 생성되면 프록시 오브젝트를 컨테이너에 반환
        ~> 컨테이너는 이 프록시 오브젝트를 빈으로 등록하고 사용
  • 확장된 포인트컷
    • 포인트컷은 메소드 선정과 빈 오브젝트 자체 선정 기능 두 가지 기능을 가짐
    • public interface Pointcut {
          ClassFilter getClassFilter(); // 프록시 적용할 클래스인지 확인
          MethodMatcher getMethodMatcher(); // 어드바이스를 적용할 메소드인지 확인
      }
      • 프록시를 적용할 클래스인지 판단 ~> 어드바이스를 적용할 메소드인지 확인
      • ProxyFactoryBean에서는 클래스 레벨 필터가 필요 없었지만, 모든 빈에 대해 프록시 자동 적용 대상을 선별하는 빈 후처리기 DefaultAdvisorAutoProxyCreator는 클래스와 메소드 선정 알고리즘 모두 가지고 있는 포인트컷이 필요
        • 위의 포인트컷 + 어드바이스가 결합된 어드바이저가 등록되어 있어야함
  • 포인트컷 테스트
    • @Test
      public void classNamePointcutAdvisor() {
          NameMatcherPointcut classMethodPointcut = new NameMatcherMethodPointcut() {
              public ClassFilter getClassFilter() {
                  return new ClassFilter() {
                      public boolean matches(Class<?> clazz) {
                          return clazz.getSimpleName().startsWith("HelloT"); // HelloT로 시작하는 클래스를 선정
                      }
                  };
              }
          };
          classMethodPointcut.setMappedName("sayH*"); // sayH로 시작하는 메소드 선정
      
          // Test
          checkAdviced(new HelloTarget(), classMethodPointcut, true);
          checkAdviced(new HelloWorld(), classMethodPointcut, false;)
      }
      
      private void checkAdviced(Object target, Pointcut pointcut, boolean adviced) {
          ProxyFactoryBean pfBean = new ProxyFactoryBean();
          pfBean.setTarget(target);
          pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice()));
          Hello proxiedHello = (Hello) pfBean.getObject();
      
          if (adviced) {
              assetThat(proxiedHello.sayHello("Toby"), is("HELLO TOBY"));
              assetThat(proxiedHello.sayHi("Toby"), is("HI TOBY"));
          } else {
              assetThat(proxiedHello.sayHello("Toby"), is("Hello Toby"));
              assetThat(proxiedHello.sayHi("Toby"), is("Hi Toby"));
          }
      }
    • 포인트 컷이 클래스 필터까지 동작해서 클래스를 거르면, 프록시를 적용해도 부가기능이 제공되지 않음
      • 어차피 어떤 메소드에도 부가기능이 적용되지 않는데, 굳이 프록시를 둘 이유가 없음

6.5.2 DefaultAdvisorAutoProxyCreator의 적용

  • 클래스 필터를 적용한 포인트컷 작성
    • public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {
          public void setMappedClassName(String mappedClassName) {
              this.setClassFilter(new SimpleClassFilter(mappedClassName));
          }
      
          static class SimpleClassFilter implements ClassFilter {
              String mappedName;
      
              private SimpleClassFilter(String mappedName) {
                  this.mappedName = mappedName;
              }
      
              @Override
              public boolean matches(Class<?> clazz) {
                  return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName()); // 와일드카드 문자열 비교를 지원
              }
          }
      }
  • 어드바이저를 이용하는 자동 프록시 생성기 등록
    • 다른 빈에서 참조되거나 코드에서 빈 이름으로 조회될 필요가 없기 때문에, id 애트리뷰트는 생략하고 class만 입력하여 사용 가능
  • 포인트컷 등록
    • 프로퍼티에 mapping될 클래스 이름과 메소드 이름의 패턴을 설정
  • 어드바이스와 어드바이저
    • 어드바이스와 어드바이저 모두 설정을 변경할 필요가 없지만, 사용되는 방법이 바뀜
    • DefaultAdvisorAutoProxyCreator에 의해 자동수집되고, 프록시 대상 선정 과정에 참여하며, 자동 생성된 프록시에 다이내믹하게 DI돼서 동작하는 어드바이저가 됨
  • ProxyFactoryBean 제거와 서비스 빈의 원상복구
    • 간접적으로 사용되던 userServiceImpl 빈의 아이디를 userService로 재설정
      • 더 이상 명시적인 프록시 팩토리 빈을 등록하지 않기 때문 ~> ProxyFactoryBean 타입 빈 삭제 가능
  • 자동 프록시 생성기를 사용하는 테스트
    • 강제 예외 발생용 TestUserService 클래스를 빈으로 등록하기
      • 문제점
        • TestUserService가 UserServiceTest 내부에 정의된 스태틱 클래스
          • 스태틱 멤버 클래스를 설정에 등록할 때는, $를 붙여 사용
        • 포인트컷이 트랜잭션 어드바이스를 적용해주는 대상 클래스 이름 패턴에 불일치
          • 클래스 이름을 TestUserServiceImpl로 수정
          • users 리스트에서 예외를 발생시킬 기준 id를 고정 상수값으로 수정
      • userService를 parent로 등록하고, @Autowired를 통해 testUserService를 DI 받도록 수정
  • 자동 생성 프록시 확인
    • 트랜잭션이 필요한 빈에 트랜잭션 부가기능이 적용됐는지 확인
      • 트랜잭션이 커밋되는 경우 적용 여부를 확인하기 힘듦 ~> 예외 상황 롤백에 대한 테스트 필요
    • 아무 빈에나 트랜잭션 부가기능이 적용된 것은 아닌지 확인
      • 프록시 자동생성기가 어드바이저 빈에 연결해둔 포인트컷 클래스 필터를 이용해 원하는 빈에만 프록시를 생성했는지 확인
      • 포인트컷 빈의 클래스 이름 패턴을 변경해서 testUserService 빈에 트랜잭션이 적용되지 않게해서 확인
      • 자동생성된 프록시 확인
        • getBean("userService")로 가져온 오브젝트의 타입은 TestUserService 타입이 아니라 JDK의 Proxy 타입
        • @Test
          public void advisorAutoProxyCreator() {
              assertThat(testUserService, is(Proxy.class));
          }

6.5.3 포인트컷 표현식을 이용한 포인트컷

  • 스프링의 리플렉션 API 사용
    • 장점
      • 클래스와 메소드 이름, 패키지, 파라미터, 리턴 값, 애노테이션, 구현 인터페이스, 상속 클래스 등의 정보를 쉽게 파악 가능
    • 단점
      • 코드 작성이 번거롭고, 메타정보 비교 방법을 조건이 달라질 때마다 포인트컷 구현 코드를 수정해야함
  • 스프링의 포인트컷 표현식
    • 일종의 표현식 언어로 포인트컷 작성
    • AspectJExpressionPointcut 클래스 사용
      • 포인트컷 표현식을 이용해 클래스와 메소드 선정 알고리즘을 한 번에 지정
      • AspectJ의 일부 문법을 확장해서 사용해서, AspectJ 포인트컷 표현식이라고도 함
    • 포인트컷 테스트용 클래스 생성
      • package springbook.leaningtest.spring.pointcut;
        
        public class Target implements TargetInterface {
            public void hello() {}
            public void hello(String a) {}
            public int minus(int a, int b) throws RuntimeException {return 0;}
            public int plus(int a, int b) {return 0;} // 이상 4개는 override한 메소드
            public void method() {} // 클래스 내부에서 자체 정의한 메소드
        }
  • 포인트컷 표현식 문법
    • 포인트컷 지시자를 이용해서 작성
      • execution()이 대표적으로 사용됨
        • execution([접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | *..*, ...) [throws 예외 패턴])
      • 메소드 시그니처를 이용한 포인트컷 표현식 테스트
        • @Test
          public void methodSignaturePointcut() throws SecurityException, NoSuchMethodException {
              AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
              pointcut.setExpression("execution(public int springbook.learningtest.spring.pointcut.Target.minus(int,int) throws java.lang.RuntimeException)");
          
              // 
          
              .minus()
              assertThat(pointcut.getClassFilter().matches(Target.class) && 
                        pointcut.getMethodMatcher().matches(
                        Target.class.getMethod("minus", int.class, int.class), null), is(true));
          
              // Target.plus()
              assertThat(pointcut.getClassFilter().matches(Target.class) &&
                        pointcut.getMethodMatcher().matches(
                        Target.class.getMethod("plus", int.class, int.class), null), is(false));
          }
  • 포인트컷 표현식 테스트
    • 메소드 시그니처를 사용한 포인트 표현식 중, 접근 제한자 패턴, 클래스 타입 패턴, 예외 패턴은 옵션이기 때문에 생략 가능
      • execution(int minus(int, int))와 같이 간략하게 표현 가능
        • 리턴 타입 제한을 없애고 싶다면, int 대신 * 와일드카드를 적용 가능
        • 파라미터 개수와 타입을 무시하려면, (int, int) 부분을 (...)로 적용 가능
        • 선정 조건을 다 없애고 모든 메소드를 다 허용하는 포인트 컷이라면 execution(* *(..))과 같이 사용 가능
    • 포인트컷과 메소드를 비교해주는 테스트 헬퍼 메소드
      • public void pointcutMatches(String expression, Boolean expected, Class<?> clazz, String methodName, Class<?>... args) throws Exception {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression(expression);
        
            assertThat(pointcut.getClassFilter().matches(clazz) &&
                      pointcut.getMethodMatcher().matches(clazz.getMethod(methodName, args), null), is(expected));
        }
    • 타깃 클래스의 메소드에 대해 포인트컷 선정 여부 검사 헬퍼 메소드
      • public void targetClassPointcutMatches(String expression, boolean... expected) throws Exception{
            pointcutMatches(expression, expected[0], Target.class, "hello");
                pointcutMatches(expression, expected[1], Target.class, "hello", String.class);
                pointcutMatches(expression, expected[2], Target.class, "plus", int.class, int.class);
                pointcutMatches(expression, expected[3], Target.class, "minus", int.class, int.class);
                pointcutMatches(expression, expected[4], Target.class, "method");
        }
    • 포인트컷 표현식 테스트
      • @Test
        public void pointcut() throws Exception {
            targetClassPointcutMatches("execution(* *(..))", true, true, true, true, true);
        }
      • Target 클래스 대신 구현 대상인 interface를 선정하게되면, 해당 interface를 구현한 메소드에만 포인트 컷이 적용됨
  • 포인트컷 표현식을 이용하는 포인트컷 적용
    • 표현식
      • execution
        • 메소드의 시그니처 비교
      • bean
        • 스프링 빈의 이름으로 비교
        • 단순한 클래스와 메소드라는 기준을 넘어서는 유용한 선정 방식
      • 특정 애노테이션 타입, 메소드, 파라미터에 적용되어 있는 것을 보고 메소드를 선정하게 하는 포인트 컷도 가능
        • @annotation(Transactional);
    • 포인트컷 적용
      • 앞서 만든 NameMatchclassMethodPointcut과 같이 직접 구현 클래스를 사용하지말고, 포인트컷 표현식을 사용해서 수정
        • AspectJExpressionPointcut으로 빈을 등록하고, 프로퍼티에 mapping될 클래스 이름과 메소드 이름의 패턴을 설정하도록 수정
  • 타입 패턴과 클래스 이름 패턴
    • 단순한 클래스 이름 패턴과 포인트컷 표현식에서 사용하는 타입 패턴의 차이점
      • 포인트컷 표현식의 클래스 이름에 적용되는 패턴은 클래스 이름 패턴이 아니라 타입 패턴이기 때문에, 구현 인터페이스, 슈퍼 클래스, 해당 클래스 모두 적용됨

6.5.4 AOP란 무엇인가?

  • UserService에 트랜잭션 적용 과정 정리
  • 트랜잭션 서비스 추상화
    • 트랜잭션 경계설정 코드를 비즈니스 로직에 넣으면서 생긴 문제는 특정 트랜잭션 기술에 종속된 코드가 되는 것
      • 서비스 추상화 기법을 적용
      • 구체적인 구현 내용을 담은 의존 오브젝트를 런타임 시에 다이내믹하게 DI
  • 프록시와 데코레이터 패턴
    • 비즈니스 로직 코드에 트랜잭션을 적용하고 있다는 사실이 코드에 드러나 있는 문제
      • DI를 이용한 데코레이터 패턴 적용
        • 클라이언트가 인터페이스와 DI를 통해 접근, 데코레이터 패턴으로 트랜잭션 부가기능 부여
        • 클라이언트가 일종의 프록시 역할을 하는 데코레이터를 거쳐 타깃에 접근
  • 다이내믹 프록시와 프록시 팩토리 빈
    • 트랜잭션 기능을 부여하지 않아도 되는 메소드마저 프록시로서 위임기능이 필요 ~> 프록시 클래스를 일일이 만드는 작업의 부담 & 구현 코드 중복 문제가 존재
    • 프록시 클래스 없이도 프록시 오브젝트를 런타임 시에 만들어주도록 JDK 다이내믹 프록시 적용
      • 동일한 기능의 프록시를 여러 오브젝트에 적용할 경우 오브젝트 단위의 중복 문제는 여전히 존재
    • 프록시 팩토리 빈을 이용해서 다이내믹 프록시 생성에 DI 도입
      • 내부적으로 템플릿/콜백 패턴 활용
        • 어드바이스(부가기능), 포인트컷(선정 알고리즘)을 프록시로부터 분리 및 공유
  • 자동 프록시 생성 방법과 포인트컷
    • 프랜잭션 적용 대상 빈마다 일일이 프록시 팩토리 빈을 설정해야하는 문제
      • 스프링 컨테이너의 빈 생성 후처리 기법 적용
        • 컨테이너 초기화 시점에 자동으로 프록시 생성
        • 클래스 선정 포인트컷 사용
        • 포인트컷 표현식을 사용해서 간단한 설정만으로 적용 대상을 선정
  • 부가기능 모듈화
    • 트랜잭션 경계설정 기능이 다른 모듈의 코드에 부가적으로 부여되는 기능이어서 앞에서 학습해왔던 방법들로 간단하게 모듈화 불가
      • 다이내믹 프록시, IoC/DI 컨테이너 빈 생성 작업을 가로채서 빈 오브젝트를 프록시로 대체하는 빈 후처리 기술, 데코레이터 패턴, 자동 프록시 생성, 포인트컷 등 복잡한 기술 요구
      • 위의 기술을 이용해서 TransactionAdvice라는 이름으로 모듈화
  • AOP: 애스펙트 지향 프로그래밍
    • 애스펙트(Aspect)
      • 자체로 애플리케이션 핵심 기능은 없지만, 구성하는 중요한 한 가지 요소로, 핵심기능에 부가되어 의미를 갖는 특별한 모듈
      • 어드바이스와 포인트컷을 함께 가지고 있음
        • 앞선 예시들의 어드바이저가 단순한 형태의 Aspect
      • 핵심기능 코드 사이에 침투한 부가기능을 독립적인 모듈인 애스펙트로 구분하여 사용
        • 핵심기능은 순수하게 그 기능만을 담당, 독립적으로 구분
        • 자신이 필요한 위치에 다이내믹하게 참여하지만, 설계 및 개발은 독립적인 관점으로 진행
    • 애스펙트 지향 프로그래밍(Aspect Oriented Programming, AOP)
      • 애플리케이션의 핵심적인 기능에서 부가기능을 분리해서 Aspect 모듈로 만들어서 설계 & 개발하는 방법
      • OOP를 돕는 보조 기술
      • 애플리케이션을 특정한 관점을 기준으로 바라볼 수 있게 해준다는 의미에서 AOP를 관점 지향 프로그래밍이라고도 부름

6.5.5 AOP 적용기술

  • 프록시를 이용한 AOP
    • 프록시로 만들어 DI로 연결된 빈 사이에 적용해서 타깃 메소드 호출 과정에 참여하여 부가기능을 제공
      • 스프링 AOP는 자바의 기본 JDK와 스프링 컨테이너 외에 기술과 환경을 요구하지 않음
    • 스프링 AOP의 어드바이스(부가기능)가 적용되는 대상은 오브젝트의 메소드이며, 프록시 방식이기에 메소드 호출 과정에서 부가기능을 제공
      • InvocationHandler의 작동 방식과 마찬가지로 어드바이스가 구현하는 MethodInterceptor 인터페이스가, 프록시로부터 요청을 받아서 타깃 오브젝트의 메소드를 호출
  • 바이트코드 생성과 조작을 통한 AOP
    • 프록시 방식이 아닌 AOP
      • AOP 기술의 원조, 가장 강력한 AOP 프레임워크인 AspectJ
      • 다이내믹 프록시 방식이 아닌, 타깃 오브젝트를 뜯어고쳐서 부가기능을 직접 넣어주는 방법 사용
      • 컴파일된 타깃의 클래스 파일 자체를 수정하거나, JVM에 로딩되는 시점에 가로채서 바이트 코드를 조작
    • 프록시를 사용하지 않는 이유
      • 바이트코드를 조작해서 타깃 오브젝트를 직접 수정하면, 스프링과 같은 DI 컨테이너의 도움을 받지 않아도 AOP를 적용할 수 있기 때문
        • 컨테이너가 사용되지 않는 환경에서도 AOP를 손쉽게 적용 가능
      • 프록시 방식보다 강력하고 유연한 AOP 가능
        • 프록시를 사용하면, 부가기능 추가는 클라이언트가 호출할 때 사용하는 메소드로 한정
        • 직접 바이트코드를 조작하면 오브젝트의 생성, 필드 값의 조회 & 조작, 스태틱 초기화 등 다양한 부가기능 부여 가능
    • 고급 AOP 기술은 바이트코드 조작을 위해 JVM의 실행 옵션을 변경하거나, 별도의 바이트코드 컴파일러를 사용하거나, 특별한 클래스 로더를 사용하는 등 번거로움
      • 일반적인 AOP를 적용할 때는, 프록시 방식의 스프링 AOP로 충분

6.5.6 AOP의 용어

  • 타깃
    • 부가기능을 부여할 대상
    • 핵심 기능을 담은 클래스 or 다른 부가기능을 제공하는 프록시 오브젝트
  • 어드바이스
    • 타깃에 제공할 부가기능을 담은 모듈
    • 오브젝트로 정의하거나 메소드 레벨에서 정의해서 사용
    • MethodInterceptor처럼 메소드 호출 과정에 전반적으로 참여하는 것, 예외 발생 시에만 동작하는 등의 메소드 호출 과정의 일부만 동작하는 등 여러 종류가 존재
  • 조인 포인트
    • 어드바이스가 적용될 수 있는 위치
    • 스프링 프록시 AOP에서 메소드의 실행 단계
    • 타깃 오브젝트가 구현한 인터페이스의 모든 메소드
  • 포인트컷
    • 어드바이스를 적용할 조인 포인트를 선별하는 작업 or 기능을 정의한 모듈
    • 스프링 AOP의 조인 포인트는 메소드 실행 ~> 스프링의 포인트컷은 메소드 선정 기능
    • excution으로 시작하고, 메소드 시그니처를 비교하는 방법을 주로 이용
  • 프록시
    • 클라이언트와 타깃 사이에 존재하며 부가기능을 제공하는 오브젝트
    • DI를 통해 타깃 대신 클라이언트에게 주입, 클라이언트의 메소드 호출을 대신 받아 타깃에 위임하면서 부가기능을 부여
    • 스프링은 프록시를 통해 AOP 지원
  • 어드바이저
    • 포인트컷 + 어드바이스 쌍의 오브젝트
    • 어떤 부가기능(어드바이스)을 어디에(포인트컷) 전달할 것인지 알고있는 AOP의 가장 기본이 되는 모듈
    • 스프링 AOP에서만 사용되는 용어
      • 일반 AOP에서는 사용 X
  • 에스펙트
    • AOP의 기본 모듈
    • 한 개 이상의 포인트컷과 어드바이스 조합으로 만들어지며, 보통 싱글톤 형태의 오브젝트
    • 스프링의 어드바이저는 아주 단순한 Aspect

AOP 네임스페이스

  • 스프링 AOP를 적용하기 위해 추가한 어드바이저, 포인트컷, 자동 프록시 생성기 같은 빈들은 애플리케이션 로직을 담은 빈과 달리, 스프링 컨테이너에 의해 자동으로 인식돼서 특별한 작업을 위해 사용됨
  • 스프링의 프록시 방식을 AOP에 적용하기 위한 빈 등록
    • 자동 프록시 생성기
      • 스프링의 DefaultAdvisorAutoProxyCreator 클래스를 빈으로 등록
      • 다른 빈을 DI하지 않고, 자신도 DI 되지 않으며 독립적으로 존재
      • Application Context가 빈 오브젝트를 생성하는 과정에 빈 후처리기로 참여
      • 빈으로 등록된 어드바이저를 이용해 프록시를 자동으로 생성
    • 어드바이스
      • 부가기능을 구현한 클래스를 빈으로 등록
      • TransactionAdvice는 AOP 관련 빈 중 유일하게 직접 구현한 클래스를 사용
    • 포인트컷
      • 스프링의 AspectJExpressionPointcut을 빈으로 등록하고, expression 프로퍼티에 포인트컷 표현식을 넣어 사용
    • 어드바이저
      • 스프링의 DefaultPointcutAdvisor 클래스를 빈으로 등록해서 사용
      • 어드바이스와 포인트컷을 프로퍼티로 참조하고, 자동 프록시 생성기에 의해 자동 검색되어 사용됨
  • AOP 네임스페이스
    • 스프링은 AOP와 관련된 태그를 정의해둔 aop 스키마를 제공
      • 별도의 네임스페이스를 지정해서 디폴트 네임스페이스의 <bean> 태그와 구분해서 사용
      • 설정 파일에 springframwork의 aop를 등록해서 사용
      • <aop:config>, <aop:pointcut>, <aop:advisor> 세 태그를 정의하면 빈이 자동으로 등록
      • transactionAdvice를 제외한 AOP 관련 빈들은 의미를 잘 드러내는 독립적인 전용 태그를 사용하도록 권장
  • 어드바이저 내장 포인트컷
    • AspectJ 포인트컷 표현식을 활용하는 포인트컷은 expression 프로퍼티를 설정해서 사용
    • 포인트컷은 어드바이저에 참조돼야 사용됨
      • 독립적으로 태그를 분리하지 않고 어드바이저 태그와 결합해서 사용가능
      • 포인트컷을 내장하면 두 개의 빈이 등록됨
반응형