관리 메뉴

HAMA 블로그

[코틀린 코딩 습작] Annotation & Reflection 본문

Kotlin

[코틀린 코딩 습작] Annotation & Reflection

[하마] 이승현 (wowlsh93@gmail.com) 2021. 5. 15. 19:08

Dynamic Proxy & Reflection 1

class Tuple3 (val name:String, val age: Int, val rate: Double){
    fun size(): Int {
        return 3
    }
    fun getValue(index: Int): Any?{
        return when(index) {
            0 -> name
            1 -> age
            2 -> rate
            else -> null
        }
    }
}

class RemoteService{
    fun action(req: Any?): Tuple3 {
       //do something
        return Tuple3("tom", 1, 1.2)
    }
}

///

data class Row(val name: String, val age: Int, val rate: Double)
interface Table {
    fun getRow1(req: String): Row
    fun getRow2(req: String): List<String>
}

class TableHandler: InvocationHandler {
    
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        val methodName = method?.name
        val service = RemoteService()
        if(methodName == "getRow1"){
           val returns = service.action(args)
           return cast(returns, method.genericReturnType)
        }
        return null
    }
    
    fun cast (value : Any?, typeClass: Type): Any?{
        if(value?.let{value::class.java} == typeClass){
            return value
        }
        
        return when{
            value is Tuple3 ->  {
                Reflection.createKotlinClass(typeClass as Class<*>).primaryConstructor
                    ?.takeIf{it.parameters.size == value.size() }
                    ?.let { it.call(*it.parameters.mapIndexed{ i, parameter -> cast(value.getValue(i), parameter.type.javaType) }.toTypedArray())}
                    ?:value
            }
            
            else -> when{
                typeClass == Void.TYPE -> null
                typeClass == Int::class.java -> when (value){
                    is Long -> value.toInt()
                    else -> value
                }
                typeClass == Double::class.java -> when(value) {
                    is Int -> value.toDouble()
                    else -> value 
                }
                else -> value
            }
        }
    }
}

fun main() {
    val table = Proxy.newProxyInstance(Table::class.java.classLoader, arrayOf(Table::class.java), TableHandler()) as Table
    println(table.getRow1("1"))
}


Dynamic Proxy & Reflection 2

import java.lang.reflect.*
import kotlin.jvm.internal.Reflection
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.javaType

data class Tuple(val name: String, val age: Int, val rate: Double){
    fun size(): Int{
        return 3
    }
    fun get(index: Int): Any? {
        return when(index) {
            0 -> name
            1 -> age
            2 -> rate
            else -> null
        }
    }
}

class RemoteService() {
    fun getRow1(req: Request): Tuple{
        //do somthing
        println("remote call : ${req.method} ")
        println("remote received : ${req.param} ")
        return Tuple("hama",22,0.5)
    }
    
    fun getRow2(req: Request): Array<String>{
        //do somthing
        println("remote call : ${req.method} ")
        println("remote received : ${req.param} ")
        return arrayOf("hama","22","0.5")
    }
}

///////////


data class Request(val method: String, val param: Any?)
data class Row1(val name: String, val age: Int, val rate: Double)


interface Table {
    fun getRow1(key: String): Row1
    fun getRow2(key: String): List<String>
}

class TableHandler : InvocationHandler {
    
    override fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
        val methodName = method.name
        return if (method.declaringClass.isInterface) {
            val req = Request(methodName, args)
            val returns = RemoteService().getRow2(req)
            return cast(returns, method.genericReturnType)
        } else {
            method.invoke(this, *(args ?: emptyArray()))
        }
    }
    
    fun cast(value: Any?, typeClass: Type): Any? {
        if (value?.let { it::class.java } == typeClass) {
            return value
        }
        
        fun Type.isCollection(): Boolean {
            return when (this) {
                is ParameterizedType -> (this.rawType as? Class<*>)?.let { Collection::class.java.isAssignableFrom(it) }
                    ?: false
                is Class<*> -> Collection::class.java.isAssignableFrom(this)
                else -> false
            }
        }
        
        return when {
            value is Tuple -> {
                Reflection.createKotlinClass(typeClass as Class<*>).primaryConstructor
                    ?.takeIf { it.parameters.size == value.size() }
                    ?.let {
                        it.call(*it.parameters.mapIndexed { i, parameter ->
                            cast(
                                value.get(i),
                                parameter.type.javaType
                            )
                        }.toTypedArray())
                    }
                    ?: value
            }
            
            value?.let { it::class.java.isArray } ?: false -> {
                when {
                    typeClass.isCollection() -> (value as Array<*>)
                        .map { element ->
                            cast(
                                element,
                                (typeClass as? ParameterizedType)?.actualTypeArguments?.let { argument -> argument[0] }!!
                            )
                        }
                        .toList()
                        .also { println(it) }
                    else ->
                        Reflection.createKotlinClass(typeClass as Class<*>).primaryConstructor
                            .also { println(it) }
                            ?.let { it.call(value) }
                            ?: value
                    
                }
            }
            else -> when {
                typeClass == Void.TYPE -> null
                typeClass == Int::class.java -> when (value) {
                    is Long -> value.toInt()
                    else -> value
                }
                typeClass == Double::class.java -> when (value) {
                    is Int -> value.toDouble()
                    else -> value
                }
                else -> value
            }
        }
    }
}


fun main() {
    val loader = Table::class.java.classLoader
    val table = Proxy.newProxyInstance(loader, arrayOf(Table::class.java), TableHandler()) as Table
//    val result = table.getRow1("12")
//    println(result.name)
//    println(result.age)
//    println(result.rate)

    println(table.getRow2("12"))
}

 

Dynamic Proxy & Reflection & Annotation 

import kotlin.jvm.internal.Reflection
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.javaType
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.lang.reflect.Type
import kotlin.reflect.KClass


//

object AnnotationUtils {
    fun <T : Annotation> findAnnotation(method: Method, annotation: Class<T>) : T? {
        return findAnnotation(method.declaringClass, method, annotation)
    }

    @Suppress("UNCHECKED_CAST")
    fun <T : Annotation> findAnnotation(clazz: Class<*>, method: Method, annotation: Class<T>) : T? {
        val annotatedMethods = clazz.methods.filter { it.signatureEquals(method) }
            .flatMap { it.annotations.filter { a -> annotation.isInstance(a) }.map { a -> a as T } }
        return if (annotatedMethods.isEmpty()) {
            val children = clazz.interfaces.mapNotNull { findAnnotation(it, method, annotation) }
            if (children.isEmpty()) {
                clazz.superclass?.let { findAnnotation(it, method, annotation) }
            } else {
                children.first()
            }
        } else {
            annotatedMethods.first()
        }
    }
    fun <T : Annotation> hasAnnotation(clazz: Class<*>, method: Method, annotation: Class<T>): Boolean {
        return null != findAnnotation(clazz, method, annotation)
    }


    private fun Method.signatureEquals(other: Method): Boolean {
        return name == other.name && parameterTypes.contentEquals(other.parameterTypes)
    }
}

//
// ============= remote service =============//

data class Tuple(val name: String, val age: Int, val rate: Double){
    fun size(): Int{
        return 3
    }
    fun get(index: Int): Any? {
        return when(index) {
            0 -> name
            1 -> age
            2 -> rate
            else -> null
        }
    }
}

data class Request(val method: String, val param: Any?)
data class Response( val returns: Any?)
annotation class Converter(val value: KClass<out Action>)

class RemoteService() {
    fun getRow(req: Request): Tuple{
        //do somthing
        println("remote call : ${req.method} ")
        println("remote received : ${req.param} ")
        return Tuple("hama",22,0.5)
    }

}

interface Action {
    fun doAction(args: Array<out Any>?): Any?
}


fun CreateAction(method: Method, args : Array<out Any>?): Action {
    val converterClazz = AnnotationUtils.findAnnotation(method, Converter::class.java)!!
    val obj = converterClazz.value.primaryConstructor?.call()
    return obj as Action
}

class ProxyInvocationHandler()
    : InvocationHandler {

    override fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
        val methodName = method.name
        return if (method.declaringClass.isInterface) {
            val action = CreateAction(method, args)
            val req = Request(methodName, action.doAction(args))

            val remote =  RemoteService()
            val returns = remote.getRow(req)
            return cast(returns, method.genericReturnType)
        } else {
            (args.let { method.invoke(this, *(args ?: emptyArray())) } ?: method.invoke(this))
        }
    }

    fun cast( value : Any? , typeClass: Type): Any? {
        if (value?.let { it::class.java } == typeClass) {
            return value
        }
        return when {
            value is Tuple -> {

                Reflection.createKotlinClass(typeClass as Class<*>).primaryConstructor
                        ?.takeIf { it.parameters.size == value.size() }
                        ?.let { it.call(*it.parameters.mapIndexed { i, parameter -> cast(value.get(i) , parameter.type.javaType) }.toTypedArray()) }
                        ?:value

            }
            else ->  when {
                typeClass == Void.TYPE -> null
                typeClass == Int::class.java -> when (value) {
                    is Long -> value.toInt()
                    else -> value
                }
                typeClass == Double::class.java -> when (value) {
                    is Int -> value.toDouble()
                    else -> value
                }
                else -> value
            }
        }
    }
}




data class DataTableTupleRow(val name : String, val age: Int, val rate : Double)


interface Table {
    fun getRow(param : String): DataTableTupleRow
}
interface DataTable: Table {

    @Converter(DataConvertor::class)
    override fun getRow(param : String): DataTableTupleRow

    class DataConvertor : Action {
        override fun doAction(args: Array<out Any>?): Any? {
            val arg = args?.get(0)
            return (arg as String).toInt()
        }
    }

}

fun main() {
    val loader = DataTable::class.java.classLoader
    val handler = ProxyInvocationHandler()
    val table = Proxy.newProxyInstance(loader, arrayOf(DataTable::class.java) , handler) as Table
    val result = table.getRow("12")
    println(result)

}

진행중..

 

Comments