ํ๋ก์ ํฉํ ๋ฆฌ
์คํ๋ง์ ํ๋ก์ ํฉํ ๋ฆฌ๋ ์ธํฐํ์ด์ค์ ๊ตฌ์ฒด ํด๋์ค์ ๋ํ ํ๋ก์๋ฅผ ๋ชจ๋ ์ ์ฉํ๊ธฐ ์ํด์ JDK ๋์ ํ๋ก์์ CGLIB๋ฅผ ํตํด ์ํฉ์ ๋ง๊ฒ ํ๋ก์๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
์ฆ ๊ฐ๋ฐ์๋ ์ธํฐํ์ด์ค์ธ์ง, ๊ตฌ์ฒด ํด๋์ค์ธ์ง์ ์ ๊ฒฝ์ฐ์ง ์๊ณ ๋, ํ๋ก์๋ฅผ ์ป์ ์ ์๊ฒ ๋๋ ๊ฒ์ ๋๋ค.
Advice
์คํ๋ง ํ๋ก์ ํฉํ ๋ฆฌ๋ JDK ๋์ ํ๋ก์์ CGLIB๋ฅผ ๋ชจ๋ ์ง์ํ๋ค๊ณ ํ์์ต๋๋ค.
JDK ๋์ ํ๋ก์๋ InvocationHandler๋ฅผ ํตํด, CGLIB๋ MethodInterceptor๋ฅผ ํตํด ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ด๋ ํ๋ก์ ํฉํ ๋ฆฌ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ํด์ ์ด ๋์ ์ค๋ณตํด์ ๋ง๋ค์ด์ผ ํ๋ค๋ ๋ฌธ์ ์ ์ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์คํ๋ง์ Advice๋ผ๋ ๊ฐ๋ ์ ๋์ ํ์์ต๋๋ค.
๊ฐ๋จํ JDK ๋์ ํ๋ก์๊ฐ ์ ๊ณตํ๋ InvocationHandler์ CGLIB๊ฐ ์ ๊ณตํ๋ MethodInterceptor์ ์ถ์ํ ๊ฐ๋ ์ด๋ผ ์๊ฐํ ์ ์์ต๋๋ค.
Advice ๋ง๋ค๊ธฐ
Advice๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ผ๋, ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ MethodInterceptor๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค.
(CGLIB์ MethodInterceptor๊ฐ ์๋ org.aopalliance.interceptor์ MethodInterceptor์ ๋๋ค.)
ํด๋น ์ธํฐํ์ด์ค๋ Interceptor๋ฅผ ์์๋ฐ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ Interceptor๋ Advice๋ฅผ ์์๋ฐ์ต๋๋ค.
MethodInterceptor์ invoke() ๋ฉ์๋๋ ๋งค๊ฐ๋ณ์๋ก MethodInvocation์ ๋ฐ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ์ธํฐํ์ด์ค๋ getMethod๋ฅผ ํตํด Method์ ๋ํ ์ฌ๋ฌ๊ฐ์ง ์ ๋ณด๋ค์ ๊ฐ์ง Mehod ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
์์
class TimeAdvice : MethodInterceptor{
val log = logger<TimeAdvice>()
override fun invoke(invocation: MethodInvocation): Any? {
val startTime = System.currentTimeMillis()
val result = invocation.proceed()
val endTime = System.currentTimeMillis()
log.info("resultTime = {}", endTime - startTime)
return result
}
}
ํด๋น ์ฝ๋์๋ target์ ํตํด ํ๋ก์๊ฐ ์๋์ ํด๋์ค๋ฅผ ํธ์ถํ๋ ์ฝ๋๊ฐ ์์ต๋๋ค.
์ด๋ invocation.proceed()์์ ์๋์ผ๋ก ํธ์ถ๋ฉ๋๋ค.
์คํ๋ง์ ํ๋ก์ ํฉํฐ๋ฆฌ๊ฐ ํ๋ก์๋ฅผ ์์ฑํ๋ ์์ ์ invocation ์์ ์ด๋ฏธ ๋ฉ์๋ ์ ๋ณด์ target(์๋ณธ ํด๋์ค) ์ ๋ณด๋ฅผ ์ธํ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ ๊ฒ์ ๋๋ค.
์์ ํ ์คํธ
interface ServiceInterface {
fun call()
}
class ServiceImpl : ServiceInterface {
override fun call() {
println("ServiceImpl.call()!@!!!!!")
}
}
class ProxyFactoryTest {
@Test
@DisplayName("์ธํฐํ์ด์ค๊ฐ ์์ผ๋ฉด JDK ๋์ ํ๋ก์ ์ฌ์ฉ")
fun interfaceProxy() {
//given
val target = ServiceImpl()
val proxyFactory = ProxyFactory(target)
proxyFactory.addAdvice(TimeAdvice())
val proxy = proxyFactory.proxy as ServiceInterface
println("targetClass = ${target.javaClass} ")
println("proxyClass = ${proxy.javaClass} ")
proxy.call()
Assertions.assertThat(AopUtils.isAopProxy(proxy)).isTrue
Assertions.assertThat(AopUtils.isCglibProxy(proxy)).isTrue
Assertions.assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse
}
}
ํ๋ก์ ํฉํฐ๋ฆฌ์ ํ๋ก์ ์์ฑ ๊ธฐ์ ์ ํ
- ๋์์ ์ธํฐํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ : JDK ๋์ ํ๋ก์
- ๋์์ ์ธํฐํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ : CGLIB
- proxyTargetClass = true ์ธ ๊ฒฝ์ฐ : ์ธํฐํ์ด์ค ์ฌ๋ถ์ ์๊ด์์ด CGLIB
์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ AOP๋ฅผ ์ ์ฉํ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก proxyTargetClass = true๋ก ์ค์ ํ์ฌ ์ฌ์ฉํฉ๋๋ค.
์ฆ ์ค์ ์ ๋ฐ๊พธ์ง ์๋ ํ CGLIB๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ณ์ ์ฌ์ฉ๋ฉ๋๋ค.
Reference
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop
Core Technologies
In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do
docs.spring.io
์คํ๋ง ํต์ฌ ์๋ฆฌ - ๊ณ ๊ธํธ - ์ธํ๋ฐ | ๊ฐ์
์คํ๋ง์ ํต์ฌ ์๋ฆฌ์ ๊ณ ๊ธ ๊ธฐ์ ๋ค์ ๊น์ด์๊ฒ ํ์ตํ๊ณ , ์คํ๋ง์ ์์ ์๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค., - ๊ฐ์ ์๊ฐ | ์ธํ๋ฐ...
www.inflearn.com
'๐๏ธ Spring > AOP' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[AOP] ์คํ๋ง AOP ๊ตฌํ๋ฐฉ๋ฒ (0) | 2022.08.02 |
---|---|
[AOP] @Aspect (0) | 2022.08.01 |
[AOP] ๋น ํ์ฒ๋ฆฌ๊ธฐ(BeanPostProccessor)์AnnotationAwareAspectJAutoProxyCreator (0) | 2022.08.01 |
[AOP] ํฌ์ธํธ์ปท, ์ด๋๋ฐ์ด์ค, ์ด๋๋ฐ์ด์ (0) | 2022.08.01 |
[Spring] AOP - ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ (0) | 2022.02.12 |