Spring/Spring AOP

스프링의 프록시 팩토리 빈

레알윙 2020. 3. 4. 21:53
반응형

ProxyFactoryBean

자바에는 JDK에서 제공하는 다이내믹 프록시 외에도 편리하게 프록시를 만들 수 있도록 지원해주는 다양한 기술이 존재한다. 따라서 스프링은 일관된 방법으로 프록시를 만들 수 있게 도와주는 추상 레이어를 제공한다.  생성된 프록시는 스프링의 빈으로 등록돼야 한다. 스프링은 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공해준다.

 

스프링의 ProxyFactoryBean은 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈이다. ProxyFactoryBean은 순수하게 프록시를 생성하는 작업만을 담당하고 프록시를 통해 제공해줄 부가기능은 별도의 빈에 둘 수 있다.

 

ProxyFactortyyBean이 생성하는 프록시에서 사용할 부가기능은 MethodInterceptor 인터페이스를 구현해서 만든다. MethodInterceptor는 InvocationHanler와 비슷하지만 한가지 다른점이 있다. InvocationHandler의 invoke() 메소드는 타깃 오브젝트에 대한 정보를 제공하지 않는다. 따라서 타깃은 InvocationHanlder를 구현한 클래스가 직접 알고 있어야 한다. 반변에 MethodInterceptor의 invoke() 메소드는 ProxyFactoryBean으로부터 타깃 오브젝트에 대한 정보까지도 함께 제공받는다. 그차이 덕분에 MethodInterceptor는 타깃 오브젝트에 상관없이 독립적으로 만들어질 수 있다. 따라서 MEthodInterceptor오브젝트는 타깃이 다른 여러 프록시에서 함께 사용할 수 있고, 싱글톤 빈으로 등록 가능하다.

 

어드바이스 : 타깃이 필요없는 순수한 부가기능

MethodInterceptor를 구현한 UppercaseAdvice에는 타깃 오브젝트가 등장하지 않는다. MethodInterceptor로는 메소드 정보와 함께 타깃 오브젝트가 담긴 MethodInvocation 오브젝트가 전달된다. MethodInvocation은 타깃 오브젝트의 메소드를 실행할 수 있는 기능이 있기 때문에 MEthodInterceptor는 부가기능을 제공하는 데만 집중 할 수 있다.

 

MethodInvocation은 일종의 콜백 오브젝트로, procced() 메소드를 실행하면 타깃 오브젝트의 메소드를 내부적으로 실행해주는 기능이 있다. MethodInvocation 구현 클래스는 일종의 공유 가능한 템플릿철머 동작하는 것이다. 바로 이점이 JDK의 다이내믹 프록시를 직접 사용하는 코드와 스프링이 제공해주는 프록시 추상화 기능인 ProxyFactoryBean을 사용하는 코드의 가장 큰 차이점이자 ProxyFactoryBean의 장점이다. ProxyFactoryBean은 작은 다위의 템플릿/콜백 구조를 응용해서 작용했기 때문에 템플릿 역할을 하는 MethodInvocation 을 싱글톤으로 두고 공유할 수 있다.

 

MethodInterceptor처럼 타깃 오브젝트에 적용하는 부가기능을 담은 오브젝트를 스프링에서는 어드바이스(advice)라고 부른다.

 

ProxyFActoryBean은 기본적으로 JDK가 제공하는 다이내믹 프록시를 만들어준다. 경우에 따라서는 CGLip이라는 하는 오픈소스 바이트 코드 생성 프레임워크를 이용해 프록시를 만들기도 한다.

 

포인트컷 : 부가기능 적용대상 메소드 선정방법

MethodInterceptor는 여러 프록시가 공유해서 사용할 수 있다. 그래서 위해서 MEthodInterceptor 오브젝느는 타깃 정보를 갖고 있지 않도록 만들었다. 그 덕분에 MEthoidInterceptor를 스프리으이 싱글톤 빈으로 등록 할 수 있었다. 그런데 여기에다 트랜잭션 적용 대상 메소드 이름 패턴을 넣어주는 것은 곤란하다. 트랜잭션 적용 메소드 패턴은 프록시마다 다를 수 있기 때문에 여러 프록시가 공유하는 MEthodInterCeptor에 특정 프록시에만 적용되는 패턴을 넣으면 문제가 된다.

 

JDK 다이내믹 프록시를 이용한 방식

 

스프링 ProxyFactoryBean을 이용한 방식

 

부가기능을 가진 InvocationHandle가 타깃과 메소드 선정 알고리즘 코드에 의존하고 있다. 하지만 스프링의 ProxyFactoryBean 방식은 두가 확장 기능인 부가기능(advice)와 메소드 선정 알고리즘(Pointcut)을 활용하는 유연한 구조를 제공한다.

 

스프링은 부가기능을 제공하는 오브젝트를 어드바이스라고 부르고, 메소드 선정 알로기즘을 담은 오브젝트를 포인트 컷이라 부른다. 어드바이스와 포인트컷은 모두 프록시에 DI로 주입돼서 사용된다. 두 가지 모두 여러 프록시에서 공유가 간으하도록 만들어지기 때문에 스프링의 싱글톤 빈으로 등록이 가능하다.

 

프록시는 클라이언트로부터 요청을 받으면 먼저 포인트컷에게 부가기능을 부여할 메소드인지 확인해 달라고 요청한다. 포인크컷으로부터 부가기능을 적용할 대상 메소드인지 확인받으면 MethodIntercetor 타입의 어드바이스를 호출한다. 어드바이스는 JDK의 다이내믹 프록시의 InvocationHandler와 달리 직접 타깃을 호출하지 않는다.

 

어드바이스가 일종의 템플릿이 되고 타깃을 호출하는 기능을 갖고 있는 MethodInvocation 오브젝트가 콜백이 되는 된다. 템플릿은 한 번 만듦녀 재사용이 가능하고 여러 빈이 공유해서 사용하 ㄹ수 있듯이, 어드바이스도 독립적인 싱글톤 빈으로 등록하고 DI를 주입해서 여러 프록시가 사용하도록 만들 수 있다.

 

프록시로부터 어드바이스와 포인트컷을 독립시키고 DI를 사용하게 한 것은 전형적인 전략 패턴 구조(2020/02/13 - [Java/패턴] - Strategy Pattern(전략패턴))이다. 프록시와 ProfactoryBean 등이 변경 없이도 기능을 자유롭게 확장할 수 있는 OCP를 충실히 지키는 구조가 되는 것이다.

@Test
public void pointcutAdvisor() {
    ProxyFactoryBean pfBean = new ProxyFactoryBean();
    pfBean.setTarget(new HelloTarget()); // 메소드 이름을 비교해서 대상을 선정하는 
    										알고리즘을 제공하는 포인트컷 생성
    
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.setMappedName("syaH*"); // 이름 비교조건 설정. 
    									sayH로 시작하는 모든 메소드를 선택하게 한다.
    
    pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice()));
    // 포인트컷과 어드바이스를 advisor로 묶어서 한 번에 추가
    
    Hello proxiedHello = (Hello) pfBean.getObject();
    
    assertThat(proxiedHello.sayHello("Toby"), is("HELLO TOBY"));
    assertThat(proxiedHello.sayHi("Toby"), is("HI TOBY"));
	assertThat(proxiedHello.sayThankYou("Toby"), is("Thank You Toby")); // 부가기능(대문자변환)
    																	   적용안함
}

 

어드바이스를 등록하듯이 포인트컷도 그냥 추가될 것을 왜 굳이 별개의 오브젝트로 묶어서 사용하는 이유는 ProxyFactoryBean에는 여러 개의 어드바이스와 포인트컷이 추가될 수 있기 때문이다. 포인트컷과 어드바이스를 따로 등록하면 어떤 어드바이스(부가기능)에 대해 어떤 포인트컷(메소드 선정)을 적용할지 애매해지기 때문이다. 그래서 이 둘을 Advisor 타입의 오브젝트에 담아서 조합을 만들어 등록하는 것이다. 여러 개의 어드바이스가 등록되더라도 각각 다른 포인트컷과 조합될 수 있기 때문에 각기 다른 메소드 선정방식을 적용할 수 있다. 이렇게 어드바이스와 포인트컷을 묶은 오브젝트를 인터페이스 이름을 따서 어드바이저라고 부른다.

어드바이저 = 포인트컷(메소드선정 알고리즘) + 어드바이스(부가기능) 

 

어드바이스와 포인트컷의 재사용

ProxyFactoryBean은 스프링의 DI와 템플릿/콜백 패턴, 서비스 추상화 등의 기법이 모두 적용된 것이다. 그 덕분에 독립적이며, 여러 프록시가 공유할 수 있는 어드바이스와 포인트컷으로 확장 기능을 분리할 수 있었다.

ProxyFactoryBean, Advice, Pointcut을 적용한 구조

 

 

 

오늘은 내가 아는 단어가 나와서.. 재미있게 공부한것 같다.

내일은 스프링 AOP 에 대해서 공부하니까 재미있을것 같다

 

출처

토비의 스프링 3.1

반응형