2022. 6. 3. 11:01ใBackend/๐ฟ Spring
์ด์ ํฌ์คํ ์์ ๋์ ํ๋ก์๋ฅผ ์ ์ฉํ ๋์ ๋ฌธ์ ์ ์ ์ง์ด๋ดค๋ค.
- ์ธํฐํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ์๋ CGLIB๋ฅผ, ์๋ ๊ฒฝ์ฐ์๋ JDK ๋์ ํ๋ก์๋ฅผ ์ ์ฉํด์ผํ๋ค.
- ๋ ๊ธฐ์ ์ ๋ชจ๋ ์ฌ์ฉํ๋ ค๋ฉด, JDK ๋์ ํ๋ก์์ InvocationHandler ์ CGLIB์ MethodInterceptor๋ฅผ ์ค๋ณตํด์ ๋ง๋ค์ด ๊ด๋ฆฌํด์ผํ ๊น?
- ํน์ ์กฐ๊ฑด์์(ํํฐ) ํ๋ก์ ๋ก์ง์ ์ ์ฉํ๋ ๊ธฐ๋ฅ์ ๊ณตํต์ผ๋ก ์ ์ฉํ ์ ์์ด์ผ ํ๋ค.
์คํ๋ง์์ ์ ๊ณตํ๋ ์ถ์ํ๋ ํ๋ก์ ๊ธฐ์ : ProxyFactory
์คํ๋ง์ ๋์ ํ๋ก์๋ฅผ ํตํฉํ์ฌ ์์ฑํด ์ฃผ๋ ํ๋ก์ ํฉํ ๋ฆฌ(Proxy Factory) ๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ด์ ์๋ ์ธํฐํ์ด์ค์ ์ ๋ฌด์ ๋ฐ๋ผ JDK ๋์ ํ๋ก์๋ CGLIB ์ ์ ํํด์ผ ํ๋ค๋ฉด, ์ด์ ๋ Proxy Factory ํ๋๋ก ํธ๋ฆฌํ๊ฒ ๋์ ํ๋ก์๋ฅผ ์์ฑํ ์ ์๋ค. Proxy Factory ์ ์์กด ๊ด๊ณ์ ์ฌ์ฉ ํ๋ฆ์ ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
Advice , Pointcut ๋์
์คํ๋ง์ Advice ๋ผ๋ ์๋ก์ด ๊ฐ๋ ์ ๋์ ํ๋ฉด์, ์ํฉ์ ๋ฐ๋ผ JDK ๋์ ํ๋ก์๋ CGLIB ๋ฅผ ๋ฐ๋ก ์์ฑํด์ผํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค. ๊ฐ๋ฐ์๋ InvocationHandler ๋ MethodInterceptor ๋ฅผ ์ ๊ฒฝ์ฐ์ง ์๊ณ , Advice ๋ง ์์ฑํ๋ฉด ๋๋ค. ( Advice ๋ ๊ฐ๋ฐ์๊ฐ ์ ์ฉํ๊ณ ์ถ์ ๋ถ๊ฐ ๊ธฐ๋ฅ ๋ก์ง์ด๋ผ ์๊ฐํ์ )
ํ๋ก์ ํฉํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ด๋ถ์์ Advice ๋ฅผ ํธ์ถํ๋ ์ ์ฉ InvocationHandler, MethodInterceptor ๋ฅผ ๋ด๋ถ์์ ์ฌ์ฉํ๋ค.
์ด์ ์ FilterHandler ๋ฅผ ๋ง๋ค์ด, ํน์ ์กฐ๊ฑด์ ๋ง๋ ๋ฉ์๋์๋ง ํ๋ก์๋ฅผ ์ ์ฉํ ์ ์ด ์๋ค. ํ๋ก์ ํฉํ ๋ฆฌ์์๋ Pointcut ์ด๋ผ๋ ๊ฐ๋ ์ ๋์ ํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ์ผ๊ด์ฑ ์๊ฒ ํด๊ฒฐํ๋ค. ์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด์ Advice ์ Pointcut ์ ๋ ์ดํดํด๋ณด์.
Advice, Pointcut ์์ ์ฝ๋
Advice ๋ ๋ถ๊ฐ๊ธฐ๋ฅ ๋ก์ง์ ๋ด๋นํ๋ค๊ณ ํ๋ค. ์ฌ๊ธฐ์ TimeAdvice ๋ ๋ฉ์๋์ ์คํ ์๊ฐ์ ์ฒดํฌํ์ฌ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด๋นํ๋ค. Advice ๋ MethodInterceptor
๋ฅผ ์์๋ฐ์ invoke(MethodInvocation invocation)
๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํ๋๋ฐ, ์ด ๋ invocation.proceed()
๋ฅผ ํธ์ถํ๋ฉด targe ํด๋์ค๋ฅผ ํธ์ถํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํด๋ฐ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด์ ํฌ์คํ
๊ณผ ๋ฌ๋ฆฌ target ์ ์ ๋ณด๊ฐ ๋ณด์ด์ง ์๋๋ค. ์ด์ ์๋ ์์ฑ์๋ก target ์ ์ฃผ์
๋ฐ์์ง๋ง, MethodInterceptor ๋ฅผ ์ฌ์ฉํ๋ฉด, MethodInvocation invocation ๋ด๋ถ์ target ์ ํด๋์ค ์ ๋ณด๊ฐ ๋ชจ๋ ํฌํจ๋ผ ์๋ค.
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@Slf4j
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("TimeProxy ์คํ");
long startTime = System.currentTimeMillis();
// ๊ธฐ์กด
//Object result = method.invoke(target, args);
// ๋ณ๊ฒฝ : MethodInterceptor ์์ ํ
Object result = invocation.proceed();
// target ์ ์๋์ผ๋ก ์ฐพ๊ณ ์ธ์๋ฅผ ๋๊ธด๋ค. -> ์ค์ ํ๊ฒ์ ํธ์ถํ๋ค.
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy ์ข
๋ฃ resultTime={}", resultTime);
return result;
}
}
์ฃผ์ํ ์ ์, TimeAdvice ๊ฐ ์์๋ฐ๋ MethodInterceptor
๊ฐ CGLIB์ MethodInterceptor ์ ์ด๋ฆ์ด ๊ฐ์ผ๋ฏ๋ก ํจํค์ง ์ด๋ฆ์ ์ฃผ์ํด์ผํ๋ค. Advice ๋ฅผ ์ ์ฉํ๊ธฐ ์ํด์๋ org.aopalliance.intercept
์คํ๋ง AOP ๋ชจ๋ ๋ด๋ถ์ ์๋ ๊ฒ์ ์ฌ์ฉํ๋ค.
์ด์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ Advice ๋ฅผ ProxyFactory ์ ์ ๋ฌํ๋ฉด proxy ๊ฐ ๋ฐํ๋๊ณ ์ ์ฉ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Advice - ProxyFactory ์ ์ฉ (1) : ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ๋ target ์ proxy ์์ฑ
- ์ฐ์ Proxy ๋ฅผ ์ ์ฉํ ๋์ (target) ๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์์ผํ๋ค. ์ฌ๊ธฐ์๋ ServiceInterface ํ์ ์ ServiceImpl ์ ์ธ์คํด์ค๋ฅผ target ์ผ๋ก ์ง์ ํ๋ค. ํด๋น ์ธ์คํด์ค๋ ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ๊ณ ์์์ ์ธ์งํ์
- ๊ทธ ๋ค์ ProxyFactory() ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋ ์ธ์๋ก target ๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ค๋ค. ํ๋ก์ ํฉํ ๋ฆฌ๋ ์ด ์ธ์คํด์ค ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ด๋ถ์์ ํ๋ก์๋ฅผ ์์ฑํ๋ค.
- ์ฐ๋ฆฌ๊ฐ ๋๊ฒจ์ค ์ธ์คํด์ค๋ ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์๋ค๊ณ ์ธ๊ธํ๋ค. ์ฆ target ์ธ์คํด์ค์ ์ธํฐํ์ด์ค๊ฐ ์๋ค๋ฉด ProxyFactory ๋ด๋ถ์์ JDK ๋์ ํ๋ก์๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๊ณ , ๊ทธ๋ ์ง ์๋ค๋ฉด CGLIB ๋ฅผ ํตํด ๋์ ํ๋ก์๋ฅผ ์์ฑํ๋ค.
proxyFactory.addAdvice()
: ์ด์ ํ๋ก์๊ฐ ์ฌ์ฉํ ๋ถ๊ฐ๊ธฐ๋ฅ ๋ก์ง์ addAdvice() ์ ์ธ์๋ก ๋๊ฒจ์ค๋ค. ์ฐ๋ฆฌ๊ฐ ๋ง๋ TimeAdvice ์ ์ธ์คํด์ค๋ฅผ ๋๊ฒจ์ฃผ์. ( JDK ๋์ ํ๋ก์๊ฐ ์ ๊ณตํ๋ InvocationHandler ์ CGLIB๊ฐ ์ ๊ณตํ๋ MethodInterceptor์ ๊ฐ๋ ๊ณผ ์ ์ฌ)- ๋ถ๊ฐ๊ธฐ๋ฅ ๊น์ง ์ค์ ์ ๋ง์น proxyFactory ๋
proxyFactory.getProxy()
๋ก ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋๋ค.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("์ธํฐํ์ด์ค๊ฐ ์์ผ๋ฉด JDK ๋์ ํ๋ก์ ์ฌ์ฉ")
void interfaceProxy() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// target ์ ๋ฃ์ผ๋ฉด์ ํ๋ก์ ํฉํ ๋ฆฌ๋ฅผ ๋ง๋ ๋ค. ์ฆ ํ๋ก์ ํฉํ ๋ฆฌ๋ ์ด๋ฏธ ํ๊ฒ์ ์ ๋ณด๋ฅผ ์๊ณ ์๋ค.
// ํ๋ก์์ ํธ์ถ๋์(target)์ ํ๋ก์ ํฉํ ๋ฆฌ๋ฅผ ์์ฑํ ๋ ์ธ์๋ก ๋๊ธฐ๋ ๊ฒ.
proxyFactory.addAdvice(new TimeAdvice()); // advice ์ถ๊ฐ -> ํ๋ก์๊ฐ ์ฌ์ฉํ ๋ถ๊ฐ ๋ก์ง์ ์ค์ ํ๋ค. (advice : ์กฐ์ธ์ ํด์ค๋ค.)
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save(); // ํ๋ก์ ๊ฐ์ฒด์ ์์ฒด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด, advice ๋ก ์ถ๊ฐ๋ (๋ก๊ทธ) ๋ก์ง์ด ์ํ๋๋ค.
// save() ์ ์ ์๋ "save ํธ์ถ" ์ด๋ ๋ก๊ทธ๊ฐ ๋ฐ๋ก ์ฐํ๋ ๊ฒ์ด ์๋๋ผ, TimeAdvice() ๋ด๋ถ์์ invocation.proceed() ํ ๋ save()์ ๋ก๊ทธ๊ฐ ์ฐํ๋ค,.
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue(); // ์ธํฐํ์ด์ค๊ฐ ์กด์ฌํ๋ฏ๋ก JDK dynamic proxy
assertThat(AopUtils.isCglibProxy(proxy)).isFalse();
}
}
์คํ ๊ฒฐ๊ณผ
Advice - ProxyFactory ์ ์ฉ (2) : ๊ตฌ์ฒด ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ํ๋ก์ ์์ฑ
(1) ์์ ์ฝ๋์ ํฌ๊ฒ ๋ฌ๋ผ์ง ๊ฒ์ ์๋ค. ์ฐจ์ด์ ์ด๋ผ๋ฉด ์ธํฐํ์ด์ค๊ฐ ์๋ ๊ตฌ์ฒดํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค๋ ์ ์ด๋ค. ๋๊ฐ์ด ํ๋ก์ ํฉํ ๋ฆฌ๋ฅผ ์ฌ์ฉํด ํ๋ก์๋ฅผ ์์ฑํ ํ , ํ ์คํธ์ฝ๋๋ฅผ ํตํด ์ด๋ค ๊ฒฐ๊ณผ๊ฐ ๋์ค๋์ง ํ์ธํด๋ณด์.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("๊ตฌ์ฒด ํด๋์ค๋ง ์์ผ๋ฉด CGLIB ์ฌ์ฉ")
void concreteProxy() {
ConcreteService target = new ConcreteService();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdvice());
ConcreteService proxy = (ConcreteService) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.call();
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
}
assertThat(AopUtils.isCglibProxy(proxy)).isTrue() ๊ฐ ํต๊ณผํ๋ ๊ฒ์ ๋ณด์ CGLIB ๋ฅผ ํ์ฉํด ํ๋ก์๊ฐ ์์ฑ๋์์ ํ์ธํ ์ ์๋ค.
Advice - ProxyFactory ์ ์ฉ (3) : ProxyTargetClass ์ต์ ์ฌ์ฉ
target ์ธ์คํด์ค๋ ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ง๋ง, setProxyTargetClass(true)
์ต์
์ ์ฌ์ฉํ๋ฉด JDK ๋์ ํ๋ก์ ๋์ ๊ฐ์ ๋ก CGLIB ๋ฅผ ์ฌ์ฉํด ํ๋ก์๋ฅผ ์์ฑํ ์ ์๋ค.
@Slf4j
public class ProxyFactoryTest {
@Test
@DisplayName("ProxyTargetClass ์ต์
์ ์ฌ์ฉํ๋ฉด ์ธํฐํ์ด์ค๊ฐ ์์ด๋ CGLIB๋ฅผ ์ฌ์ฉํ๊ณ , ํด๋์ค ๊ธฐ๋ฐ ํ๋ก์ ์ฌ์ฉ")
void proxyTargetClass() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.setProxyTargetClass(true);
// proxy ๋ฅผ ๋ง๋๋๋ฐ target ํด๋์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค๊บผ์ผ! ๋ผ๊ณ ์ค์
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save();
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
}
์ ๋ฆฌ
ํ๋ก์ ํฉํ ๋ฆฌ์ ์ถ์ํ ๋๋ถ์ ๊ตฌ์ฒด์ ์ธ CGLIB, JDK ๋์ ํ๋ก์ ๊ธฐ์ ์ ์์กดํ์ง ์๊ณ ํธ๋ฆฌํ๊ฒ ๋์ ํ๋ก์๋ฅผ ์์ฑํ ์ ์๊ฒ ๋๋ค. ๋ํ ํ๋ก์์ ๋ถ๊ฐ๊ธฐ๋ฅ ๋ก์ง๋ ํน์ ๊ธฐ์ ์ ์ข ์๋์ง ์๊ณ , Advice ํ๋๋ก ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์์๋ค. ์ด๊ฒ์ ํ๋ก์ ํฉํ ๋ฆฌ ๋ด๋ถ์์ JDK ๋์ ํ๋ก์์ธ ๊ฒฝ์ฐ InvocationHandler ๊ฐ Advice ๋ฅผ ํธ์ถํ๊ณ , CGLIB ์ธ ๊ฒฝ์ฐ MethodInterceptor ๊ฐ Advice ๋ฅผ ํธ์ถํ๋๋ก ๊ฐ๋ฐ๋์๊ธฐ ๋๋ฌธ์ด๋ค .
์ฝ๋ ๋ฐ ์๋ฃ ์ถ์ฒ
'Backend > ๐ฟ Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] ๋น ํ์ฒ๋ฆฌ๊ธฐ, Bean Postprocessor (0) | 2022.06.04 |
---|---|
[Spring] Pointcut, Advise, Advisor ๊ธฐ๋ณธ ๊ฐ๋ ์ ์์๋ณด์ (0) | 2022.06.03 |
[Spring] ๋์ ํ๋ก์ ๊ธฐ์ (3) - CGLIB (0) | 2022.06.02 |
[Spring] ๋์ ํ๋ก์ ๊ธฐ์ (2) - JDK DynamicProxy (0) | 2022.06.02 |
[Spring] ๋์ ํ๋ก์ ๊ธฐ์ (1) - ๋ฆฌํ๋ ์ (0) | 2022.06.02 |