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)
}
진행중..