web study/배경지식

jdk dynamic proxy | cglib

65살까지 코딩 2023. 12. 19. 21:38
728x90
반응형

jdk dynamic proxy와 cglib 

둘은 어디에 쓰이는 걸까? 이름에서 볼 수 있다시피 proxy를 동적으로 만들어주는 라이브러리다. 
jdk dynamic proxy는 java의 reflection을 이용해서 프록시를 만들고

cglib는 바이트코드 수준에서 코드를 조작하여 프록시를 생성한다.

jdk dynamic proxy는 어떻게 사용할까

jdk dynamic proxy는 java의 invocationHandler interface를 구현해서 사용한다.

proxy의 target은 interface만 들어갈 수 있다.  

 

예시

class LogInvocationHandler(
    private val target : Any,
    private val logService: LogServiceDirtyCode
) : InvocationHandler {


    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any?>?): Any {

        val className = method?.declaringClass?.name ?: ""
        val methodName = method?.name ?: ""

        var trace: Trace? = null

        return runCatching {
            trace = logService.begin(className + methodName)
            val methodArgs = args ?: emptyArray()
            method?.invoke(target, *methodArgs) ?: throw IllegalArgumentException("mehtod null")
        }.onSuccess {
            logService.finish(trace!!)
        }.onFailure {
            logService.execption(trace!!, it)
            throw it
        }.getOrThrow()
    }
}

 

생성한 invocation을 Proxy.newInstance를 이용해서 사용할 수 있다.

fun itemControllerJdkProxy(logService: LogServiceDirtyCode): ItemControllerJdkProxy {
    return Proxy.newProxyInstance(
        ItemControllerJdkProxy::class.java.classLoader,
        arrayOf(ItemControllerJdkProxy::class.java),
        LogInvocationHandler(ItemControllerJdkProxyImpl(itemServiceJdkProxy(logService)), logService)
    ) as ItemControllerJdkProxy
}

 

 

cglib는 어떻게 사용할까?

cglib는 code generator library의 약자이다 (참고) 

cglib는 spring proxy의 MethodInterceptor interface를 구현해 사용한다.

target으로 구현 객체만 들어올 수 있다.(최근에는 인터페이스도 들어 올 수 있게 바꾼 것 같다.)

 

예시

class LogMethodInterceptor(
    private val logService: LogServiceDirtyCode,
    private val target: Any
): MethodInterceptor {
    override fun intercept(obj: Any?, method: Method?, args: Array<out Any>?, methodProxy: MethodProxy?): Any {
        val className = method?.declaringClass?.name ?: ""
        val methodName = method?.name ?: ""
        if(methodName.contains("toString")){
            return target.toString();
        }
        var trace: Trace? = null

        return runCatching {
            trace = logService.begin(className + methodName)
            val methodArgs = args ?: emptyArray()
            methodProxy?.invoke(target, methodArgs) ?: throw IllegalArgumentException("mehtod null")
        }.onSuccess {
            logService.finish(trace!!)
        }.onFailure {
            logService.execption(trace!!, it)
            throw it
        }.getOrThrow()


    }
}

이는 Enhacer.create를 통해 proxy 를 만들어 사용할 수있다.

fun itemRepositoryProxy(logService: LogServiceDirtyCode): ItemRepositoryRealSubject {
    return Enhancer.create(
        ItemRepositoryRealSubject::class.java,
        LogMethodInterceptor(logService, ItemRepositoryRealSubject())
    ) as ItemRepositoryRealSubject
}

 

 

728x90
반응형