You will be fine

<토비의스프링> 6.4~6.5 AOP(2)

by BFine
반응형

6.4 스프링의 프록시 팩토리빈

  • ProxyFactoryBean

    • 프록시를 생성해 빈오브젝트로 등록

    • TxProxyFactoryBean 과 차이점

      • 순수하게 프록시를 생성하는 작업만 담당
      • 부가기능은 별도의 빈
    • 부가기능은 MethodInterceptor 인터페이스를 구현

      • InvocationHandler와 달리 타깃이 InvocationHandler를 구현한 클래스정보를 몰라도됨
    • 코드 수정

        @Test
        public void proxyFactoryBean() {
            ProxyFactoryBean pfBean = new ProxyFactoryBean();
            pfBean.setTarget(new HelloTarget());
            pfBean.addAdvice(new UppercaseAdvice());
      
            Hello hello = (Hello) pfBean.getObject(); 
        }
      
        static class UppercaseAdvice implements MethodInterceptor {
      
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                        String ret = (String) invocation.proceed();
                        // MethodInvocation은 메소드 정보와 타깃오브젝트를 알고있기 때문에 
                        // 전달할 필요가없다.
      
                return ret.toUpperCase(); 
            }
        }
      
  • 어드바이스 : 타깃이 필요없는 순수한 부가기능

    • MethodInvocation
      • 타깃 오브젝트의 메소드를 실행할 수 있는 기능이 존재
      • 일종의 콜백 오브젝트, proceed() 를 실행하면 타깃의 메소드를 내부적으로 실행
      • 얘를 구현한클래스는 공유가능한 템플릿처럼 동작
    • ProxyFactoryBean
      • MethodInvocation을 싱글톤으로 두고 공유
      • addAdvice() 를 통해여러개의 MethodInterceptor 추가 가능
      • 앞서 문제점인 추가할때마다 빈추가 문제 해결
      • 인터페이스 자동검출 기능을 통해 타깃오브젝트가 구현한 인터페이스 정보를 알아냄
    • MethodInterceptor 처럼 타깃에 적용하는 순수 부가기능을 담은 객체를 어드바이스라 한다
  • 포인트컷 : 부가기능 대상 메소드 선정방법

      ProxyFatoryBean
             ▽
                  생성
      다이나믹 프록시  -> 포인트컷 (대상메서드 선정) 
                                      -> 어드바이스 -> invocation 콜백 -> 타깃 
    • DI로 주입받는 스프링빈

      @Test
      public void pointcutAdvisor() {
      ProxyFactoryBean pfBean = new ProxyFactoryBean();
      pfBean.setTarget(new HelloTarget());
      
      NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
      pointcut.setMappedName("sayH*");
      
      pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice()));
        // 별개로 두는 이유는 ProxyFactoryBean에 여러개의 어드바이스와 
        // 포인트컷이 추가 될 수 있기 떄문
      
        Hello proxiedHello = (Hello) pfBean.getObject();
      }
    • 어드바이스와 포인트컷을 묶은 객체를 어드바이저

  • ProxyFactoryBean 적용

    • TransationAdvice

        public class TransactionAdvice implements MethodInterceptor {
            PlatformTransactionManager transactionManager;
      
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                TransactionStatus status = this.transactionManager.getTransaction(
                                                                                    new DefaultTransactionDefinition());
                try {
                    Object ret = invocation.proceed(); // 타깃실행
                    this.transactionManager.commit(status);
                    return ret;
      
                } catch (RuntimeException e) {
                    this.transactionManager.rollback(status);
                    throw e;
                }
            }
        }
    • xml 설정

        <bean id="transactionAdvice" class="user.service.TransactionAdvice">
            <property name="transactionManager" ref="transactionManager" />
        </bean>
      
        <bean id="transactionPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
            <property name="mappedName" value="upgrade*" />
        </bean>
      
        <bean id="transactionAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="advice" ref="transactionAdvice" />
            <property name="pointcut" ref="transactionPointCut" />
        </bean>
      
        <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
              <property name="target" ref="userServiceImpl" />
              <property name="interceptorNames">
                  <list>
                      <value>transactionAdvisor</value>
                  </list>
              </property>
        </bean>

6.5 스프링 AOP

  • 자동 프록시 생성

    • 타킷마다 거의 비슷한 내용의 프록시팩토리빈 설정정보를 추가해야함..

      → 자동으로 각 타깃 빈에 대한 프록시를 만들 수 없을까?

  • 빈후처리기를 이용항 자동프록시 생성기

    • BeanPostProcesser 인터페이스를 구현해 만드는 빈후처리기

    • 스프링빈으로 만들어 빈을 가공

    • DefaultAdvisorAutoProxyCreator 스프링에서 제공하는 빈후처리기

      • 어드바이저를 이용한 자동 프록시 생성기

      • 빈이 생성될떄마다 빈후처리기로 보내 후처리작업을 요청

      • 빈 프로퍼티를 강제로 수정, 별도의 초기화 작업가능

        ⇒ 이를 활용해 프록시를 빈으로 대신 등록

    • 동작

      1. 빈후처리기가 있으면 스프링은 빈을 만들때 후처리기로 보낸다.
      2. 모든 어드바이저내의 포인트컷으로 프록시 적용대상인지 확인
      3. 빈에대한 프록시를 만들고 어드바이저를 연결
      4. 원래 빈 대신 프록시를 컨테이너에게 반환
  • 포인트컷

    • 메소드 Matcher와 클래스 필터를 가지고있음
  • DefaultAdvisorAutoProxyCreator

    • 어드바이저를 이용하는 자동프록시 생성기 등록

        //빈이름으로 조회될 필요가 없는 빈은 아이디 등록이 필요없다.
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
      
        <bean id="transactionPointCut" class="service.NameMatchClassMethodPointcut">
            <property name="mappedClassName" value="*ServiceImpl" />
            <property name="mappedName" value="upgrade*" />
        </bean>
    • 자동 프록시 테스트

      • UserService 등록

        <bean id="testUserService" 
        class="UserServiceTest$TestUserServiceImpl" parent="userService" />
      • $기호는 스태틱 멤버클래스를 지정할때 사용

      • 특정 테스트 클래스에서만 사용되는 거면 스태틱 멤버클래스로 정의하는 것이 편리

    • 자동프록시 확인

      자동으로 된다고하면 한번쯤 직접확인해 보는 습관을 가지자!

      • 확인사항
        1. 트랙잭션 부가기능이 적용되는지 → 롤백테스트
        2. 아무 빈에나 적용된게 아닌지 확인
  • 포인트컷 표현식을 이용한 포인트컷

    • 위는 이름패턴을 각각 클래스 필터, 메서드 매처로 비교해서 선정함
    • 세밀하게 적용하려면 리플렉션 API 를 쓸수 있으나 번거로움
    • 표현식 언어를 사용해 포인트컷을 작성하는 것이 포인트컷 표현식
    • 스프링의 포인트컷 표현식은 AspectJ라는 프레임워크에서 제공한걸 사용
    • 표현식 문법
      • execution() 가장 대표적으로 사용
      • [접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | ".. ", ... )[throws 예외 패턴]
      • 리플렉션의 Class.getMethod 로 메소드의 풀 시그니처 확인가능
    • 적용
      • 특정 어노테이션으로 포인트컷 선정하는 것도 가능 @Transactioal
      • 단점은 런타임시점까지 문법의 검증이나 기능확인이 되지않음
      • 클래스 이름 패턴이 아니라 타입패턴으로 등록됨
        • 슈퍼클래스 구현인테페이스도 포함
  • AOP란 무엇인가?

    • 트랙잭션 서비스 추상화

      • 트랙잭션 기술이 비즈니스로직에 종속

        → 인터페이스와 DI로 분리

    • 프록시 데코레이터 패턴

      • 투명한 부가기능 부여를 가능하게 하는 데코레이터

        : 클라이언트 → {데코레이터[트랜잭션] - 타킷}

    • 다이나믹 프록시와 팩토리빈

      • 비즈니스 로직 인터페이스의 모든 메소드마다 기능부여하는 코드추가
        • 불필요한 메서드에도 일일히 구현
      • 프록시 클래스를 자동으로 만들자!
        • 런타임시 JDK 다이나믹프록시
          • 동일기능 프록시를 여러 타킷에 적용시 타깃단위로 중복
        • 스프링팩토리빈
          • 어드바이스 + 포인트컷을 프록시에서 분리 및 공유사용
    • 자동프록시 생성

      • 빈마다 일일히 팩토리빈 등록해야함
        • 빈생성 후처리기 기법 적용 → 포인트컷이라는 독립정보로 완전분리
    • 부가기능 모듈화

      • 서로 낮은 결합도, 유연성, 확장성
      • 부가기능이기 때문에 스스로 독립적으로 존재하기는 힘듬
    • AOP: 에스팩트 지향 프로그래밍

      • 부가기능 모듈을 에스펙트라고 부르기 시작

      • 핵심기능은 아니지만 중요한 요소

      • 여러 부가기능은 결국 핵심 기능과 함께 어우러져 동작

      • 핵심기능에서 부가기능을 분리해서 에스팩트로 개발하는 방법

        → 에스팩트 지향 프로그래밍

      • OOP를 돕는 보조적인 기술

  • AOP 적용기술

    • 프록시를 이용한 AOP
      • 타깃 메서드에 다이나믹하게 적용해주기 위해 중요한 역할
      • 스프링 AOP는 프록시 방식이다
    • 바이트코드 조작 AOP (AspectJ)
      • JVM 로딩시점을 가로채서 바이트코드를 조작
      • 컨테이너 사용되지 않는 환경에서도 가능
      • 다양한 작업에 부가기능 부여가능
  • AOP 용어

    • 조인포인트
      • 어드바이스가 적용될수 있는 위치
    • 어드바이스
      • 부가기능을 담은 모듈
반응형

블로그의 정보

57개월 BackEnd

BFine

활동하기