스프링의 프록시 팩토리 빈
이전까지 자바를 이용하여 기존 코드 수정없이 부가기능을 추가해주었다. 이제 스프링에서는 어떠한 방식으로 추가해주는지 살펴보자.
ProxyFactoryBean
자바 JDK에서 제공하는 다이나믹 프록시 외에도 편리하게 프록시를 만들 수 있도록 지원해주는 다양한 기능이 있다. 스프링은 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공해준다.
package com.waug.common.currency.service;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Developer : 김태현
* Date : 2020/02/01
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class DynamicProxyTest {
@Test
public void proxyFactoryBean() {
ProxyFactoryBean pfBean = new ProxyFactoryBean();
pfBean.setTarget(new HelloTarget());
pfBean.addAdvice(new UppercaseAdvice());
Hello hello = (Hello) pfBean.getObject(); // proxy 가져오기
assertThat(hello.sayHello("Ted"), is("HELLO TED"));
}
static interface Hello {
String sayHello(String name);
String sayHi(String name);
String sayThankYou(String name);
}
static class UppercaseAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return invocation.proceed().toString().toUpperCase();
}
}
static class HelloTarget implements Hello {
@Override
public String sayHello(String name) {
return "Hello" + name;
}
@Override
public String sayHi(String name) {
return "sayHi" + name;
}
@Override
public String sayThankYou(String name) {
return "sayThankYou" + name;
}
}
}
어드바이스 : 타깃이 필요없는 순수한 부가기능
JDK 다이나믹 프록시와의 차이점
- 타깃 오브젝트가 등장하지 않음 -> 부가기능에만 집중 가능
- addAdvice() -> 여러 개의 부가기능을 제공해주는 프록시를 만들 수 있음 ( 기존에는 프록시, 프록시팩토리 빈을 추가해줬어야 하는 문제 해결 )
MethodInvocation은 일종의 콜백 오브젝트로, proceed() 메소드를 실행하면 타킷 오브젝트의 메소드를 내부적으로 실행해주는 기능이 있다
- MethodInvocation 구현 클래스는 일종의 공유 가능한 템플릿처럼 동작하는 것
Advice : MethodInterceptor처럼 타깃 오브젝트에 적용하는 부가기능을 담은 오브젝트를 스프링에서는 어드바이저라고 부른다.
포인트컷 : 부가기능 적용 대상 메소드 선정 방법
MethodInterceptor는 여러 프록시가 공유해서 사용할 수 있다. 그러기 위해서는 해당 오브젝트에 타깃 정보를 갖지 않도록 만들었다. 그 덕분에 싱글톤 빈으로 등록할 수 있다.
그렇다면 앞에서 본 메소드 선정 기능은 어디에 넣어야할까?
' 함께 두기 곤란한 성격이 다르고 변경 이유와 시점이 다르고, 생성 방식과 의존관계가 다른 코드가 함께 있다면 분리해준다 ' 방식을 적용하자.
@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()));
Hello proxiedHello = (Hello) pfBean.getObject();
assertThat(proxiedHello.sayHello("Ted"), is("HELLO TED"));
}
Advisor(어드바이즈) = Pointcut(메소드 선정 알고리즘) + Advice(부가기능)
위의 코드를 보면 아래 특징 덕분에 OCP를 지키는 코드가 되었다.
- 프록시로부터 어드바이스와 포인트컷을 독립시켜서 DI 구조 => 전략 패턴
( 프록시 <- 어드바이스,포인트컷(strategy) )
'Back-End > 토비의 스프링3' 카테고리의 다른 글
7장. 스프링 핵심 기술의 응용 (0) | 2019.07.25 |
---|---|
7.4. 인터페이스 상속을 통한 안전한 기능확장 (0) | 2019.07.25 |
7.2. 인터페이스의 분리와 자기참조 빈 & 7.3. 서비스 추상화 적용 (0) | 2019.07.04 |
7.6 스프링 3.1 DI & 7.7 정리 (0) | 2019.07.04 |
6.8장 - 트랜잭션 지원 테스트 (0) | 2019.06.20 |