mirror of
https://github.com/tursom/TursomServer.git
synced 2025-03-13 11:20:10 +08:00
faster proxy
This commit is contained in:
parent
acf16136a4
commit
d170678182
BIN
ts-core/ts-proxy/TestClass.class
Normal file
BIN
ts-core/ts-proxy/TestClass.class
Normal file
Binary file not shown.
BIN
ts-core/ts-proxy/TestClass2.class
Normal file
BIN
ts-core/ts-proxy/TestClass2.class
Normal file
Binary file not shown.
BIN
ts-core/ts-proxy/TestClass_InvocationHandler.class
Normal file
BIN
ts-core/ts-proxy/TestClass_InvocationHandler.class
Normal file
Binary file not shown.
BIN
ts-core/ts-proxy/TestClass_MethodAccess.class
Normal file
BIN
ts-core/ts-proxy/TestClass_MethodAccess.class
Normal file
Binary file not shown.
0
ts-core/ts-proxy/a
Normal file
0
ts-core/ts-proxy/a
Normal file
@ -11,6 +11,7 @@ dependencies {
|
||||
api(project(":ts-core"))
|
||||
api(project(":ts-core:ts-reflectasm"))
|
||||
api(group = "cglib", name = "cglib", version = "3.3.0")
|
||||
implementation(group = "net.bytebuddy", name = "byte-buddy", version = "1.12.22")
|
||||
implementation(group = "org.apache.commons", name = "commons-lang3", version = "3.8.1")
|
||||
testApi(group = "junit", name = "junit", version = "4.13.2")
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.core.context.Context
|
||||
|
||||
|
||||
class ListProxyContainer(
|
||||
private val proxyList: MutableCollection<ProxyMethod> = ArrayList(),
|
||||
) : MutableProxyContainer {
|
||||
override var lastModify: Long = System.currentTimeMillis()
|
||||
private set
|
||||
override val context: Context = ProxyContainer.contextEnv.newContext()
|
||||
|
||||
override fun addProxy(proxy: ProxyMethod) {
|
||||
lastModify = System.currentTimeMillis()
|
||||
proxyList.add(proxy)
|
||||
}
|
||||
|
||||
override fun addAllProxy(proxy: Collection<ProxyMethod>?): Boolean {
|
||||
lastModify = System.currentTimeMillis()
|
||||
return proxyList.addAll(proxy!!)
|
||||
}
|
||||
|
||||
override fun removeProxy(proxy: ProxyMethod) {
|
||||
lastModify = System.currentTimeMillis()
|
||||
proxyList.remove(proxy)
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<ProxyMethod> {
|
||||
lastModify = System.currentTimeMillis()
|
||||
return proxyList.iterator()
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
interface MutableProxyContainer : ProxyContainer {
|
||||
fun addProxy(proxy: ProxyMethod)
|
||||
fun addAllProxy(proxy: Collection<ProxyMethod>?): Boolean
|
||||
fun removeProxy(proxy: ProxyMethod)
|
||||
}
|
@ -1,24 +1,27 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.core.allMethodsSequence
|
||||
import cn.tursom.core.isPrivate
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import net.sf.cglib.proxy.Enhancer
|
||||
import net.sf.cglib.proxy.Factory
|
||||
import cn.tursom.proxy.container.ListProxyContainer
|
||||
import cn.tursom.proxy.container.MutableProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.function.ProxyMethod
|
||||
import cn.tursom.proxy.interceptor.LocalCachedProxyInterceptor
|
||||
import cn.tursom.proxy.interceptor.ProxyInterceptor
|
||||
import cn.tursom.reflect.final
|
||||
import net.sf.cglib.proxy.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object Proxy {
|
||||
object CallSuperException : Exception()
|
||||
|
||||
val callSuper = object : ThreadLocal<Boolean>() {
|
||||
override fun initialValue(): Boolean = throw CallSuperException
|
||||
override fun get(): Boolean = try {
|
||||
super.get()
|
||||
} catch (_: CallSuperException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var defaultContainer: () -> MutableProxyContainer = { ListProxyContainer() }
|
||||
private val cache = ConcurrentHashMap<Class<*>, Class<*>>()
|
||||
val directAccessorKey = ProxyContainer.ctxEnv.newKey<Any>()
|
||||
|
||||
private val methodProxyFieldSignature = MethodProxy::class.java.getDeclaredField("sig1").also {
|
||||
it.isAccessible = true
|
||||
it.final = false
|
||||
}
|
||||
|
||||
fun getContainer(obj: Any): ProxyContainer? {
|
||||
if (obj !is Factory) return null
|
||||
@ -38,18 +41,88 @@ object Proxy {
|
||||
builder: (Class<T>) -> T,
|
||||
): Pair<T, MutableProxyContainer> {
|
||||
val target = getCachedTarget(clazz)
|
||||
|
||||
val directAccessor = builder(target)
|
||||
val obj = builder(target)
|
||||
injectCallback(obj as Factory, container)
|
||||
|
||||
container.target = obj
|
||||
container.ctx[directAccessorKey] = directAccessor
|
||||
|
||||
injectCallback(obj as Factory, container, directAccessor)
|
||||
|
||||
return obj to container
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> get() = get(T::class.java)
|
||||
inline fun <reified T : Any> get(
|
||||
inline operator fun <reified T : Any> get(
|
||||
argumentTypes: Array<out Class<*>>,
|
||||
arguments: Array<out Any?>,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get(T::class.java, argumentTypes, arguments, container)
|
||||
|
||||
inline operator fun <reified T : Any, reified A1 : Any?> get(
|
||||
a1: A1,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get<T>(arrayOf(A1::class.java), arrayOf(a1), container)
|
||||
|
||||
inline operator fun <
|
||||
reified T : Any,
|
||||
reified A1 : Any?,
|
||||
reified A2 : Any?,
|
||||
> get(
|
||||
a1: A1, a2: A2,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get<T>(
|
||||
arrayOf(A1::class.java, A2::class.java),
|
||||
arrayOf(a1, a2),
|
||||
container,
|
||||
)
|
||||
|
||||
inline operator fun <
|
||||
reified T : Any,
|
||||
reified A1 : Any?,
|
||||
reified A2 : Any?,
|
||||
reified A3 : Any?,
|
||||
> get(
|
||||
a1: A1, a2: A2, a3: A3,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get<T>(
|
||||
arrayOf(A1::class.java, A2::class.java, A3::class.java),
|
||||
arrayOf(a1, a2, a3),
|
||||
container,
|
||||
)
|
||||
|
||||
inline operator fun <
|
||||
reified T : Any,
|
||||
reified A1 : Any?,
|
||||
reified A2 : Any?,
|
||||
reified A3 : Any?,
|
||||
reified A4 : Any?,
|
||||
> get(
|
||||
a1: A1, a2: A2, a3: A3, a4: A4,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get<T>(
|
||||
arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java),
|
||||
arrayOf(a1, a2, a3, a4),
|
||||
container,
|
||||
)
|
||||
|
||||
inline operator fun <
|
||||
reified T : Any,
|
||||
reified A1 : Any?,
|
||||
reified A2 : Any?,
|
||||
reified A3 : Any?,
|
||||
reified A4 : Any?,
|
||||
reified A5 : Any?,
|
||||
> get(
|
||||
a1: A1, a2: A2, a3: A3, a4: A4, a5: A5,
|
||||
container: MutableProxyContainer = defaultContainer(),
|
||||
) = get<T>(
|
||||
arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java, A5::class.java),
|
||||
arrayOf(a1, a2, a3, a4, a5),
|
||||
container,
|
||||
)
|
||||
|
||||
operator fun <T : Any> get(clazz: Class<T>, container: MutableProxyContainer = defaultContainer()) =
|
||||
get(clazz, container, Class<T>::newInstance)
|
||||
|
||||
@ -68,23 +141,56 @@ object Proxy {
|
||||
if (interfaces.isNotEmpty()) {
|
||||
enhancer.setInterfaces(interfaces)
|
||||
}
|
||||
enhancer.setCallbackType(ProxyInterceptor::class.java)
|
||||
enhancer.setCallbackFilter { 0 }
|
||||
|
||||
val methods = clazz.allMethodsSequence.filter { !it.isPrivate() }.toList()
|
||||
|
||||
enhancer.setCallbackTypes(Array(methods.size) { MethodInterceptor::class.java })
|
||||
enhancer.setCallbackFilter(methods::indexOf)
|
||||
|
||||
return enhancer
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer()): ProxyContainer {
|
||||
fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer(), target: Any = obj): ProxyContainer {
|
||||
obj as Factory
|
||||
if (obj.getCallback(0) != null && obj.getCallback(0) != ProxyDispatcher) {
|
||||
if (obj.getCallback(0) != null && obj.getCallback(0) is ProxyInterceptor) {
|
||||
return (obj.getCallback(0) as ProxyInterceptor).container
|
||||
}
|
||||
|
||||
obj.setCallback(0, ProxyInterceptor(container))
|
||||
val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java))
|
||||
repeat(obj.callbacks.size) {
|
||||
obj.setCallback(it, LocalCachedProxyInterceptor(container, nonProxyClasses, target))
|
||||
}
|
||||
return container
|
||||
}
|
||||
|
||||
fun <T : Any> getCachedTarget(clazz: Class<T>): Class<T> = cache.computeIfAbsent(clazz) {
|
||||
newEnhancer(clazz).createClass()
|
||||
}.uncheckedCast()
|
||||
|
||||
fun <T : Any> getSuperCaller(
|
||||
obj: T,
|
||||
): T = getContainer(obj)?.ctx?.get(directAccessorKey).uncheckedCast()
|
||||
|
||||
fun addNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean {
|
||||
if (target !is Factory ||
|
||||
target.getCallback(0) == null ||
|
||||
target.getCallback(0) !is ProxyInterceptor
|
||||
) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.add(nonProxyClass)
|
||||
}
|
||||
|
||||
fun removeNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean {
|
||||
if (target !is Factory ||
|
||||
target.getCallback(0) == null ||
|
||||
target.getCallback(0) !is ProxyInterceptor
|
||||
) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.remove(nonProxyClass)
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.core.context.ArrayContextEnv
|
||||
import cn.tursom.core.context.Context
|
||||
|
||||
interface ProxyContainer : Iterable<ProxyMethod> {
|
||||
companion object {
|
||||
val contextEnv = ArrayContextEnv()
|
||||
|
||||
inline fun ProxyContainer.forEachProxy(action: (ProxyMethod) -> Unit) {
|
||||
for (t in this) {
|
||||
action(t)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <R> ProxyContainer.forFirstProxy(action: (ProxyMethod) -> ProxyResult<R>): ProxyResult<R> {
|
||||
for (t in this) {
|
||||
val result = action(t)
|
||||
if (result.success) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return ProxyResult.failed()
|
||||
}
|
||||
}
|
||||
|
||||
// to impl, use override val context: Context = ProxyContainer.contextEnv.newContext()
|
||||
val context: Context
|
||||
val lastModify: Long
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
object ProxyDispatcher : MethodInterceptor {
|
||||
override fun intercept(obj: Any, method: Method, args: Array<out Any>, proxy: MethodProxy): Any {
|
||||
Proxy.injectCallback(obj)
|
||||
return proxy(obj, args)
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class ProxyInterceptor(
|
||||
val container: ProxyContainer = ListProxyContainer(),
|
||||
// disable call super can save 20% time
|
||||
private val supportCallSuper: Boolean = true,
|
||||
) : MethodInterceptor {
|
||||
override fun intercept(obj: Any, method: Method, args: Array<out Any?>, proxy: MethodProxy): Any? {
|
||||
if (supportCallSuper && Proxy.callSuper.get()) {
|
||||
Proxy.callSuper.remove()
|
||||
return proxy.invokeSuper(obj, args)
|
||||
}
|
||||
|
||||
val result = ProxyRunner.onProxy(obj, container, method, args, proxy)
|
||||
return if (result.success) {
|
||||
result.result
|
||||
} else {
|
||||
proxy.invokeSuper(obj, args)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.proxy.annotation.ForEachProxy
|
||||
import cn.tursom.proxy.annotation.ForFirstProxy
|
||||
import cn.tursom.reflect.asm.ReflectAsmUtils
|
||||
import com.esotericsoftware.reflectasm.MethodAccess
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal typealias ProxyMethodHandler = (proxy: ProxyMethod, args: Array<out Any?>) -> ProxyResult<*>
|
||||
|
||||
interface ProxyMethod {
|
||||
@Throws(Throwable::class)
|
||||
fun onProxy(
|
||||
ignored: Any,
|
||||
c: ProxyContainer,
|
||||
method: Method,
|
||||
args: Array<out Any?>,
|
||||
proxy: MethodProxy
|
||||
): ProxyResult<*> {
|
||||
val handlerCacheMap = getHandlerCacheMap(javaClass)
|
||||
val handlerCache = handlerCacheMap[method]
|
||||
if (handlerCache != null) {
|
||||
return if (handlerCache.success) {
|
||||
handlerCache.result(this, args)
|
||||
} else {
|
||||
ProxyResult.failed<Any>()
|
||||
}
|
||||
}
|
||||
|
||||
val reflectAsmHandler = ReflectASMProxyMethodInvoker[this, method, handlerCacheMap]
|
||||
if (reflectAsmHandler != null) {
|
||||
return reflectAsmHandler(this, args)
|
||||
}
|
||||
|
||||
val javaReflectHandler = JavaReflectProxyMethodInvoker[this, method, handlerCacheMap]
|
||||
if (javaReflectHandler != null) {
|
||||
javaReflectHandler(this, args)
|
||||
}
|
||||
|
||||
handlerCacheMap[method] = ProxyResult.failed()
|
||||
return ProxyResult.failed<Any>()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val handlerCacheMapMap: MutableMap<
|
||||
Class<out ProxyMethod>,
|
||||
MutableMap<Method, ProxyResult<ProxyMethodHandler>>
|
||||
> = HashMap()
|
||||
|
||||
fun getHandlerCacheMap(type: Class<out ProxyMethod>): MutableMap<Method, ProxyResult<ProxyMethodHandler>> {
|
||||
var handlerCacheMap = handlerCacheMapMap[type]
|
||||
if (handlerCacheMap == null) synchronized(handlerCacheMapMap) {
|
||||
handlerCacheMap = handlerCacheMapMap[type]
|
||||
if (handlerCacheMap == null) {
|
||||
handlerCacheMap = ConcurrentHashMap()
|
||||
handlerCacheMapMap[type] = handlerCacheMap!!
|
||||
}
|
||||
}
|
||||
return handlerCacheMap!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ReflectASMProxyMethodInvoker(
|
||||
val methodAccess: MethodAccess,
|
||||
val index: Int,
|
||||
) : ProxyMethodHandler {
|
||||
companion object {
|
||||
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>) -> Any? = MethodAccess::invoke
|
||||
|
||||
operator fun get(
|
||||
proxy: ProxyMethod,
|
||||
method: Method,
|
||||
handlerCacheMap: MutableMap<Method, ProxyResult<ProxyMethodHandler>>
|
||||
): ProxyMethodHandler? {
|
||||
val reflectAsmMethod = try {
|
||||
ReflectAsmUtils.getMethod(
|
||||
proxy.javaClass,
|
||||
method.name,
|
||||
paramTypes = method.parameterTypes,
|
||||
returnType = method.returnType,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
if (reflectAsmMethod != null) {
|
||||
val (methodAccess, index) = reflectAsmMethod
|
||||
val invoker = ReflectASMProxyMethodInvoker(methodAccess, index)
|
||||
handlerCacheMap[method] = ProxyResult.of(invoker)
|
||||
return invoker
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(proxy: ProxyMethod, args: Array<out Any?>): ProxyResult<*> {
|
||||
return ProxyResult.of(fastInvoker(methodAccess, proxy, index, args))
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaReflectProxyMethodInvoker(
|
||||
val method: Method,
|
||||
) : ProxyMethodHandler {
|
||||
companion object {
|
||||
private val fastInvoker: Method.(Any, Array<out Any?>) -> Any? = Method::invoke
|
||||
|
||||
operator fun get(
|
||||
proxy: ProxyMethod,
|
||||
method: Method,
|
||||
handlerCacheMap: MutableMap<Method, ProxyResult<ProxyMethodHandler>>
|
||||
): ProxyMethodHandler? {
|
||||
var invoker: ProxyMethodHandler? = null
|
||||
|
||||
val selfMethod: Method
|
||||
try {
|
||||
var methodName = method.name
|
||||
for (annotation in method.annotations) {
|
||||
if (annotation is ForEachProxy) {
|
||||
if (annotation.name.isNotEmpty()) {
|
||||
methodName = annotation.name
|
||||
break
|
||||
}
|
||||
} else if (annotation is ForFirstProxy) {
|
||||
if (annotation.name.isNotEmpty()) {
|
||||
methodName = annotation.name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes)
|
||||
selfMethod.isAccessible = true
|
||||
|
||||
invoker = JavaReflectProxyMethodInvoker(method)
|
||||
|
||||
handlerCacheMap[method] = ProxyResult(invoker, true)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
return invoker
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(proxy: ProxyMethod, args: Array<out Any?>): ProxyResult<*> {
|
||||
return ProxyResult.of(fastInvoker(method, proxy, args))
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
typealias ProxyMethodCacheFunction = (
|
||||
obj: Any,
|
||||
c: ProxyContainer,
|
||||
method: Method,
|
||||
args: Array<out Any?>,
|
||||
proxy: MethodProxy
|
||||
) -> ProxyResult<*>
|
||||
|
||||
class ProxyMethodCache(
|
||||
private var lastModify: Long = 0,
|
||||
) {
|
||||
companion object {
|
||||
val failed: ProxyMethodCacheFunction = { _, _, _, _, _ -> ProxyResult.failed }
|
||||
}
|
||||
|
||||
private val functionMap = ConcurrentHashMap<MethodProxy, ProxyMethodCacheFunction>()
|
||||
|
||||
fun update(lastModify: Long, proxy: MethodProxy, function: ProxyMethodCacheFunction = functionMap[proxy] ?: failed) {
|
||||
if (this.lastModify != lastModify) {
|
||||
functionMap.clear()
|
||||
}
|
||||
|
||||
this.lastModify = lastModify
|
||||
functionMap[proxy] = function
|
||||
}
|
||||
|
||||
operator fun invoke(
|
||||
lastModify: Long,
|
||||
obj: Any,
|
||||
c: ProxyContainer,
|
||||
method: Method,
|
||||
args: Array<out Any?>,
|
||||
proxy: MethodProxy,
|
||||
): ProxyResult<*>? = if (lastModify != this.lastModify) {
|
||||
null
|
||||
} else {
|
||||
functionMap[proxy]?.invoke(obj, c, method, args, proxy)
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.core.uncheckedCast
|
||||
|
||||
data class ProxyResult<out R>(
|
||||
val result: R,
|
||||
val success: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
val success: ProxyResult<*> = ProxyResult<Any?>(null, true)
|
||||
val failed: ProxyResult<*> = ProxyResult<Any?>(null, false)
|
||||
|
||||
fun <R> of(): ProxyResult<R?> = success.uncheckedCast()
|
||||
fun <R> of(result: R): ProxyResult<R> = if (result == null) {
|
||||
success.uncheckedCast()
|
||||
} else {
|
||||
ProxyResult(result, true)
|
||||
}
|
||||
|
||||
fun <R> failed(): ProxyResult<R> = failed.uncheckedCast()
|
||||
}
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.proxy.ProxyContainer.Companion.forEachProxy
|
||||
import cn.tursom.proxy.ProxyContainer.Companion.forFirstProxy
|
||||
import cn.tursom.proxy.annotation.ForEachProxy
|
||||
import cn.tursom.proxy.annotation.ForFirstProxy
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.lang.reflect.Method
|
||||
import java.util.*
|
||||
|
||||
|
||||
object ProxyRunner {
|
||||
private val errMsgSearchList = arrayOf("%M", "%B", "%A")
|
||||
private val proxyMethodCacheKey = ProxyContainer.contextEnv.newKey<ProxyMethodCache>()
|
||||
.withDefault { ProxyMethodCache() }
|
||||
|
||||
/**
|
||||
* will be call when proxy method invoke.
|
||||
* 在代理方法被调用时,该方法会被调用
|
||||
*/
|
||||
@Throws(Throwable::class)
|
||||
fun onProxy(obj: Any, c: ProxyContainer, method: Method, args: Array<out Any?>, proxy: MethodProxy): ProxyResult<*> {
|
||||
val cache = c.context[proxyMethodCacheKey]
|
||||
cache(c.lastModify, obj, c, method, args, proxy)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
var handler: ProxyMethodCacheFunction? = null
|
||||
for (annotation in method.annotations) when (annotation) {
|
||||
is ForEachProxy -> {
|
||||
handler = onForeachProxy(annotation)
|
||||
break
|
||||
}
|
||||
is ForFirstProxy -> {
|
||||
handler = onForFirstProxy(annotation)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (handler == null) {
|
||||
//handler = ProxyContainerHandlerCache.callSuper
|
||||
handler = onForFirstProxy(ForFirstProxy.EMPTY)
|
||||
}
|
||||
return handler(obj, c, method, args, proxy)
|
||||
}
|
||||
|
||||
fun onForFirstProxy(forFirstProxy: ForFirstProxy): ProxyMethodCacheFunction {
|
||||
val classes = when (forFirstProxy.value.size) {
|
||||
0 -> emptyList()
|
||||
1 -> listOf(forFirstProxy.value[0].java)
|
||||
else -> forFirstProxy.value.asSequence().map { it.java }.toSet()
|
||||
}
|
||||
return { o, c, m: Method, a, p ->
|
||||
onForFirstProxy(o, c, m, a, p, forFirstProxy, classes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onForFirstProxy(
|
||||
obj: Any,
|
||||
container: ProxyContainer,
|
||||
method: Method,
|
||||
args: Array<out Any?>,
|
||||
proxy: MethodProxy,
|
||||
forFirstProxy: ForFirstProxy,
|
||||
classes: Collection<Class<*>>,
|
||||
): ProxyResult<*> {
|
||||
val result = container.forFirstProxy { p ->
|
||||
if (classes.isEmpty() || classes.stream().anyMatch { c: Class<*> -> c.isInstance(p) }) {
|
||||
val result = p.onProxy(obj, container, method, args, proxy)
|
||||
if (forFirstProxy.cache && result.success) {
|
||||
container.context[proxyMethodCacheKey].update(container.lastModify, proxy, p::onProxy)
|
||||
}
|
||||
return@forFirstProxy result
|
||||
} else {
|
||||
return@forFirstProxy ProxyResult.failed
|
||||
}
|
||||
}
|
||||
if (result.success) {
|
||||
return result
|
||||
}
|
||||
|
||||
// when request not handled
|
||||
if (forFirstProxy.must) {
|
||||
// generate error message
|
||||
var errMsg: String = forFirstProxy.errMsg
|
||||
if (errMsg.isBlank()) {
|
||||
errMsg = "no proxy handled on method %M"
|
||||
}
|
||||
val replacementList = arrayOfNulls<String>(errMsgSearchList.size)
|
||||
// todo use efficient contains
|
||||
if (errMsg.contains(errMsgSearchList[0])) {
|
||||
replacementList[0] = method.toString()
|
||||
}
|
||||
if (errMsg.contains(errMsgSearchList[1])) {
|
||||
replacementList[1] = obj.toString()
|
||||
}
|
||||
if (errMsg.contains(errMsgSearchList[2])) {
|
||||
replacementList[2] = Arrays.toString(args)
|
||||
}
|
||||
|
||||
errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList)
|
||||
val exceptionConstructor = forFirstProxy.errClass.java.getConstructor(String::class.java)
|
||||
if (forFirstProxy.cache) {
|
||||
container.context[proxyMethodCacheKey].update(container.lastModify, proxy) { _, _, _, _, _ ->
|
||||
throw exceptionConstructor.newInstance(errMsg)
|
||||
}
|
||||
}
|
||||
throw exceptionConstructor.newInstance(errMsg)
|
||||
}
|
||||
if (forFirstProxy.cache) {
|
||||
container.context[proxyMethodCacheKey].update(container.lastModify, proxy)
|
||||
}
|
||||
return ProxyResult.failed
|
||||
}
|
||||
|
||||
fun onForeachProxy(forEachProxy: ForEachProxy) = onForeachProxy(when (forEachProxy.value.size) {
|
||||
0 -> emptyList()
|
||||
1 -> listOf(forEachProxy.value[0].java)
|
||||
else -> forEachProxy.value.asSequence().map { it.java }.toSet()
|
||||
}, forEachProxy.cache)
|
||||
|
||||
private val emptyProxyList = ArrayList<ProxyMethod>()
|
||||
private fun onForeachProxy(
|
||||
classes: Collection<Class<*>>,
|
||||
cache: Boolean,
|
||||
): ProxyMethodCacheFunction = { o, c, m, a, proxy ->
|
||||
val proxyList = if (cache) ArrayList<ProxyMethod>() else emptyProxyList
|
||||
c.forEachProxy { p ->
|
||||
if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) {
|
||||
val result = p.onProxy(o, c, m, a, proxy)
|
||||
if (cache && result.success) {
|
||||
proxyList.add(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cache) {
|
||||
c.context[proxyMethodCacheKey].update(c.lastModify, proxy, onCachedForeachProxy((proxyList)))
|
||||
}
|
||||
ProxyResult.failed<Any?>()
|
||||
}
|
||||
|
||||
private fun onCachedForeachProxy(proxyList: List<ProxyMethod>): ProxyMethodCacheFunction = { o, c, m, a, proxy ->
|
||||
proxyList.forEach { p ->
|
||||
p.onProxy(o, c, m, a, proxy)
|
||||
}
|
||||
ProxyResult.failed<Any?>()
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ annotation class ForFirstProxy(
|
||||
*/
|
||||
val must: Boolean = false,
|
||||
val errMsg: String = "",
|
||||
val errClass: KClass<out RuntimeException> = RuntimeException::class,
|
||||
val errClass: KClass<out Throwable> = Throwable::class,
|
||||
val cache: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
|
@ -0,0 +1,54 @@
|
||||
package cn.tursom.proxy.container
|
||||
|
||||
import cn.tursom.core.context.Context
|
||||
import cn.tursom.proxy.function.ProxyMethod
|
||||
import cn.tursom.proxy.util.CglibUtil
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
|
||||
|
||||
class ListProxyContainer(
|
||||
private val proxyList: MutableCollection<Any> = ArrayList(),
|
||||
) : MutableProxyContainer {
|
||||
override lateinit var target: Any
|
||||
override val ctx: Context = ProxyContainer.ctxEnv.newContext()
|
||||
|
||||
private fun clearCache() {
|
||||
ctx[ProxyMethodCache.ctxKey].clear()
|
||||
}
|
||||
|
||||
override fun addProxy(proxy: Any) {
|
||||
clearCache()
|
||||
|
||||
proxyList.add(proxy)
|
||||
if (proxy is ProxyMethod) {
|
||||
proxy.onProxyAdded(this)
|
||||
}
|
||||
|
||||
clearCache()
|
||||
}
|
||||
|
||||
override fun addAllProxy(proxy: Collection<Any>?) {
|
||||
clearCache()
|
||||
|
||||
if (proxyList.addAll(proxy!!)) proxy.forEach {
|
||||
if (it !is ProxyMethod) return@forEach
|
||||
it.onProxyAdded(this)
|
||||
}
|
||||
|
||||
clearCache()
|
||||
}
|
||||
|
||||
override fun removeProxy(proxy: Any) {
|
||||
clearCache()
|
||||
|
||||
if (proxyList.remove(proxy) && proxy is ProxyMethod) {
|
||||
proxy.onProxyRemoved(this)
|
||||
}
|
||||
|
||||
clearCache()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<Any> {
|
||||
return proxyList.iterator()
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.tursom.proxy.container
|
||||
|
||||
interface MutableProxyContainer : ProxyContainer {
|
||||
override var target: Any
|
||||
|
||||
fun addProxy(proxy: Any)
|
||||
fun addAllProxy(proxy: Collection<Any>?)
|
||||
fun removeProxy(proxy: Any)
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.tursom.proxy.container
|
||||
|
||||
import cn.tursom.core.context.ArrayContextEnv
|
||||
import cn.tursom.core.context.Context
|
||||
|
||||
interface ProxyContainer : Iterable<Any> {
|
||||
companion object {
|
||||
val ctxEnv = ArrayContextEnv()
|
||||
}
|
||||
|
||||
// to impl, use override val context: Context = ProxyContainer.contextEnv.newContext()
|
||||
val ctx: Context
|
||||
val target: Any
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.tursom.proxy.container
|
||||
|
||||
import cn.tursom.proxy.util.IntMap
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
|
||||
class ProxyMethodCache {
|
||||
companion object {
|
||||
val ctxKey = ProxyContainer.ctxEnv.newKey<ProxyMethodCache>()
|
||||
.withDefault { ProxyMethodCache() }
|
||||
}
|
||||
|
||||
//private val functionMap = HashMap<Int, ProxyMethodCacheFunction>()
|
||||
private val functionMap = IntMap<ProxyMethodCacheFunction>()
|
||||
|
||||
fun clear() {
|
||||
synchronized(functionMap) {
|
||||
functionMap.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun update(proxy: MethodProxy, function: ProxyMethodCacheFunction) {
|
||||
synchronized(this) {
|
||||
functionMap[proxy.superIndex] = function
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(proxy: MethodProxy) = functionMap[proxy.superIndex]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.proxy.container
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
fun interface ProxyMethodCacheFunction : (
|
||||
Any?,
|
||||
ProxyContainer,
|
||||
Method?,
|
||||
Array<out Any?>?,
|
||||
MethodProxy?,
|
||||
) -> Any?
|
@ -0,0 +1,25 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class CachedOnForEachProxyImpl(
|
||||
private val proxyList: List<ProxyMethodCacheFunction>,
|
||||
) : ProxyMethodCacheFunction {
|
||||
override fun invoke(
|
||||
o: Any?,
|
||||
c: ProxyContainer,
|
||||
m: Method?,
|
||||
a: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
proxy!!
|
||||
|
||||
proxyList.forEach { p ->
|
||||
p(o, c, m, a, proxy)
|
||||
}
|
||||
return proxy.invokeSuper(o, a)
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
object CallSuperProxyMethodCacheFunction : ProxyMethodCacheFunction {
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
c: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
return proxy!!.invokeSuper(obj, args)
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.annotation.ForEachProxy
|
||||
import cn.tursom.proxy.annotation.ForFirstProxy
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
internal class JavaReflectProxyMethodInvoker(
|
||||
private val self: Any,
|
||||
private val method: Method,
|
||||
) : ProxyMethodCacheFunction {
|
||||
companion object {
|
||||
private val fastInvoker: Method.(Any?, Array<out Any?>?) -> Any? = Method::invoke
|
||||
|
||||
operator fun get(
|
||||
proxy: Any,
|
||||
method: Method,
|
||||
): JavaReflectProxyMethodInvoker? {
|
||||
var invoker: JavaReflectProxyMethodInvoker? = null
|
||||
|
||||
val selfMethod: Method
|
||||
try {
|
||||
var methodName = method.name
|
||||
for (annotation in method.annotations) {
|
||||
if (annotation is ForEachProxy) {
|
||||
if (annotation.name.isNotEmpty()) {
|
||||
methodName = annotation.name
|
||||
break
|
||||
}
|
||||
} else if (annotation is ForFirstProxy) {
|
||||
if (annotation.name.isNotEmpty()) {
|
||||
methodName = annotation.name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes)
|
||||
selfMethod.isAccessible = true
|
||||
|
||||
invoker = JavaReflectProxyMethodInvoker(proxy, method)
|
||||
|
||||
//handlerCacheMap[method] = ProxyResult(invoker, true)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
return invoker
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
c: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
return fastInvoker(this.method, self, args)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.annotation.ForEachProxy
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCache
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class OnForEachProxyImpl(
|
||||
forEachProxy: ForEachProxy,
|
||||
) : ProxyMethodCacheFunction {
|
||||
companion object {
|
||||
private val emptyProxyList = ArrayList<ProxyMethodCacheFunction>()
|
||||
}
|
||||
|
||||
private val classes: Collection<Class<*>> = when (forEachProxy.value.size) {
|
||||
0 -> emptyList()
|
||||
1 -> listOf(forEachProxy.value[0].java)
|
||||
else -> forEachProxy.value.asSequence().map { it.java }.toSet()
|
||||
}
|
||||
|
||||
private val cache: Boolean = forEachProxy.cache
|
||||
|
||||
override fun invoke(
|
||||
o: Any?,
|
||||
c: ProxyContainer,
|
||||
m: Method?,
|
||||
a: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
m!!
|
||||
proxy!!
|
||||
|
||||
val proxyList = if (cache) ArrayList() else emptyProxyList
|
||||
c.forEach { p ->
|
||||
if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) {
|
||||
val handler = ProxyMethod.getHandler(p, m) ?: return@forEach
|
||||
handler(o, c, m, a, proxy)
|
||||
if (cache) {
|
||||
proxyList.add(handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cache) {
|
||||
c.ctx[ProxyMethodCache.ctxKey].update(
|
||||
proxy, CachedOnForEachProxyImpl(proxyList)
|
||||
)
|
||||
}
|
||||
return proxy.invokeSuper(o, a)
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.annotation.ForFirstProxy
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCache
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Method
|
||||
import java.util.*
|
||||
|
||||
class OnForFirstProxyImpl(
|
||||
private val ffpAnnotation: ForFirstProxy,
|
||||
) : ProxyMethodCacheFunction {
|
||||
companion object {
|
||||
private val errMsgSearchList = arrayOf("%M", "%B", "%A")
|
||||
}
|
||||
|
||||
private val classes: Collection<Class<*>> = when (ffpAnnotation.value.size) {
|
||||
0 -> emptyList()
|
||||
1 -> listOf(ffpAnnotation.value[0].java)
|
||||
else -> ffpAnnotation.value.asSequence().map { it.java }.toSet()
|
||||
}
|
||||
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
container: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
method!!
|
||||
proxy!!
|
||||
|
||||
container.forEach { p ->
|
||||
if (classes.isNotEmpty() && classes.none { c: Class<*> -> c.isInstance(p) }) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val handler = ProxyMethod.getHandler(p, method) ?: return@forEach
|
||||
if (ffpAnnotation.cache) {
|
||||
container.ctx[ProxyMethodCache.ctxKey].update(proxy, handler)
|
||||
}
|
||||
return handler(obj, container, method, args, proxy)
|
||||
}
|
||||
|
||||
// when request not handled
|
||||
if (ffpAnnotation.must) {
|
||||
// generate error message
|
||||
var errMsg: String = ffpAnnotation.errMsg
|
||||
if (errMsg.isBlank()) {
|
||||
errMsg = "no proxy handled on method %M"
|
||||
}
|
||||
val replacementList = arrayOfNulls<String>(errMsgSearchList.size)
|
||||
// todo use efficient contains
|
||||
if (errMsg.contains(errMsgSearchList[0])) {
|
||||
replacementList[0] = method.toString()
|
||||
}
|
||||
if (errMsg.contains(errMsgSearchList[1])) {
|
||||
replacementList[1] = obj.toString()
|
||||
}
|
||||
if (errMsg.contains(errMsgSearchList[2])) {
|
||||
replacementList[2] = Arrays.toString(args)
|
||||
}
|
||||
|
||||
errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList)
|
||||
val exceptionConstructor = ffpAnnotation.errClass.java.getConstructor(String::class.java)
|
||||
if (ffpAnnotation.cache) {
|
||||
container.ctx[ProxyMethodCache.ctxKey].update(
|
||||
proxy, ExceptionProxyMethodCacheFunctionImpl(exceptionConstructor, errMsg)
|
||||
)
|
||||
}
|
||||
throw exceptionConstructor.newInstance(errMsg)
|
||||
}
|
||||
if (ffpAnnotation.cache) {
|
||||
container.ctx[ProxyMethodCache.ctxKey].update(
|
||||
proxy,
|
||||
CallSuperProxyMethodCacheFunction,
|
||||
)
|
||||
}
|
||||
return proxy.invokeSuper(obj, args)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExceptionProxyMethodCacheFunctionImpl(
|
||||
private val exceptionConstructor: Constructor<out Throwable>,
|
||||
private val errMsg: String,
|
||||
) : ProxyMethodCacheFunction {
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
c: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
) = throw exceptionConstructor.newInstance(errMsg)
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
interface ProxyMethod {
|
||||
fun onProxyAdded(container: ProxyContainer) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
fun onProxyRemoved(container: ProxyContainer) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
fun onProxyInvoke(
|
||||
o: Any?,
|
||||
c: ProxyContainer,
|
||||
m: Method?,
|
||||
a: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
next: ProxyMethodCacheFunction,
|
||||
): Any? {
|
||||
return next(o, c, m, a, proxy)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
|
||||
var handler = getReflectHandler(proxy, method) ?: return null
|
||||
|
||||
if (proxy is ProxyMethod) {
|
||||
handler = ProxyMethodInvoker(proxy, handler)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
private fun getReflectHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
|
||||
val reflectAsmHandler = ReflectASMProxyMethodInvoker[proxy, method]
|
||||
if (reflectAsmHandler != null) {
|
||||
return reflectAsmHandler
|
||||
}
|
||||
|
||||
val javaReflectHandler = JavaReflectProxyMethodInvoker[proxy, method]
|
||||
if (javaReflectHandler != null) {
|
||||
return javaReflectHandler
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
internal class ProxyMethodInvoker(
|
||||
private val proxyMethod: ProxyMethod,
|
||||
private val next: ProxyMethodCacheFunction,
|
||||
) : ProxyMethodCacheFunction {
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
c: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
return proxyMethod.onProxyInvoke(obj, c, method, args, proxy, next)
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.tursom.proxy.function
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import cn.tursom.reflect.asm.ReflectAsmUtils
|
||||
import com.esotericsoftware.reflectasm.MethodAccess
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
internal class ReflectASMProxyMethodInvoker(
|
||||
private val self: Any,
|
||||
private val methodAccess: MethodAccess,
|
||||
private val index: Int,
|
||||
) : ProxyMethodCacheFunction {
|
||||
companion object {
|
||||
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
|
||||
|
||||
operator fun get(
|
||||
proxy: Any,
|
||||
method: Method,
|
||||
): ReflectASMProxyMethodInvoker? {
|
||||
val reflectAsmMethod = try {
|
||||
ReflectAsmUtils.getMethod(
|
||||
proxy.javaClass,
|
||||
method.name,
|
||||
paramTypes = method.parameterTypes,
|
||||
returnType = method.returnType,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
} ?: return null
|
||||
|
||||
val (methodAccess, index) = reflectAsmMethod
|
||||
return ReflectASMProxyMethodInvoker(proxy, methodAccess, index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(
|
||||
obj: Any?,
|
||||
c: ProxyContainer,
|
||||
method: Method?,
|
||||
args: Array<out Any?>?,
|
||||
proxy: MethodProxy?,
|
||||
): Any? {
|
||||
return fastInvoker(methodAccess, self, index, args)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.tursom.proxy.interceptor
|
||||
|
||||
import cn.tursom.reflect.asm.ReflectAsmUtils
|
||||
import com.esotericsoftware.reflectasm.MethodAccess
|
||||
import net.sf.cglib.proxy.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class LocalCachedNoProxyInvocationHandler(
|
||||
val proxy: Any,
|
||||
) : InvocationHandler {
|
||||
companion object {
|
||||
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
|
||||
}
|
||||
|
||||
private var handler: (method: Method?, args: Array<out Any>?) -> Any? = DefaultHandler()
|
||||
|
||||
override fun invoke(ignore: Any, method: Method?, args: Array<out Any>?): Any? {
|
||||
return handler(method, args)
|
||||
}
|
||||
|
||||
private inner class DefaultHandler : (Method?, Array<out Any>?) -> Any? {
|
||||
override fun invoke(method: Method?, args: Array<out Any>?): Any? {
|
||||
method!!
|
||||
|
||||
val (methodAccess, index) = ReflectAsmUtils.getMethodByRegex(
|
||||
proxy.javaClass,
|
||||
"CGLIB\\\$${method.name}\\\$.*".toRegex(),
|
||||
*method.parameterTypes,
|
||||
method.returnType,
|
||||
)!!
|
||||
handler = MethodAccessHandler(methodAccess, index)
|
||||
return fastInvoker(methodAccess, proxy, index, args)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class MethodAccessHandler(
|
||||
private val methodAccess: MethodAccess,
|
||||
private val index: Int,
|
||||
) : (Method?, Array<out Any>?) -> Any? {
|
||||
override fun invoke(method: Method?, args: Array<out Any>?) = fastInvoker(methodAccess, proxy, index, args)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cn.tursom.proxy.interceptor
|
||||
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCache
|
||||
import cn.tursom.proxy.container.ProxyMethodCacheFunction
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class LocalCachedProxyInterceptor(
|
||||
container: ProxyContainer,
|
||||
nonProxyClasses: MutableSet<Class<*>>,
|
||||
val target: Any,
|
||||
) : ProxyInterceptor(container, nonProxyClasses) {
|
||||
private var cache: ProxyMethodCacheFunction? = null
|
||||
|
||||
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
|
||||
var cache = this.cache
|
||||
|
||||
if (cache != null) {
|
||||
return cache(target, container, method, args, proxy)
|
||||
}
|
||||
|
||||
val methodCache = container.ctx[ProxyMethodCache.ctxKey]
|
||||
this.cache = methodCache[proxy]
|
||||
cache = this.cache
|
||||
|
||||
if (cache != null) {
|
||||
return cache(target, container, method, args, proxy)
|
||||
}
|
||||
|
||||
return super.intercept(target, method, args, proxy)
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.proxy.interceptor
|
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class NoProxyProxyInterceptor(
|
||||
private val target: Any,
|
||||
) : MethodInterceptor {
|
||||
override fun intercept(obj: Any?, method: Method?, args: Array<out Any>?, proxy: MethodProxy): Any? {
|
||||
return proxy.invokeSuper(target, args)
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package cn.tursom.proxy.interceptor
|
||||
|
||||
import cn.tursom.proxy.annotation.ForEachProxy
|
||||
import cn.tursom.proxy.annotation.ForFirstProxy
|
||||
import cn.tursom.proxy.container.ListProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyContainer
|
||||
import cn.tursom.proxy.container.ProxyMethodCache
|
||||
import cn.tursom.proxy.function.CallSuperProxyMethodCacheFunction
|
||||
import cn.tursom.proxy.function.OnForEachProxyImpl
|
||||
import cn.tursom.proxy.function.OnForFirstProxyImpl
|
||||
import net.sf.cglib.proxy.MethodInterceptor
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Method
|
||||
|
||||
open class ProxyInterceptor(
|
||||
val container: ProxyContainer = ListProxyContainer(),
|
||||
val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java)),
|
||||
) : MethodInterceptor {
|
||||
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
|
||||
val cache = container.ctx[ProxyMethodCache.ctxKey]
|
||||
var handler = cache[proxy]
|
||||
if (handler != null) {
|
||||
return handler(obj, container, method, args, proxy)
|
||||
}
|
||||
|
||||
method!!
|
||||
|
||||
nonProxyClasses.forEach { nonProxyClass ->
|
||||
nonProxyClass.declaredMethods.forEach {
|
||||
if (it.name == method.name &&
|
||||
it.returnType.isAssignableFrom(method.returnType) &&
|
||||
it.parameterTypes.contentEquals(method.parameterTypes)
|
||||
) {
|
||||
cache.update(proxy, CallSuperProxyMethodCacheFunction)
|
||||
return proxy.invokeSuper(obj, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//var handler: ProxyMethodCacheFunction? = null
|
||||
|
||||
for (annotation in method.annotations) when (annotation) {
|
||||
is ForEachProxy -> {
|
||||
handler = OnForEachProxyImpl(annotation)
|
||||
break
|
||||
}
|
||||
|
||||
is ForFirstProxy -> {
|
||||
handler = OnForFirstProxyImpl(annotation)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (handler == null) {
|
||||
handler = OnForFirstProxyImpl(ForFirstProxy.EMPTY)
|
||||
}
|
||||
|
||||
return handler(obj, container, method, args, proxy)
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package cn.tursom.proxy
|
||||
package cn.tursom.proxy.util
|
||||
|
||||
import cn.tursom.core.final
|
||||
import cn.tursom.core.static
|
||||
import cn.tursom.reflect.asm.ReflectAsmUtils
|
||||
import net.sf.cglib.core.Signature
|
||||
import net.sf.cglib.proxy.Callback
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import java.lang.reflect.Field
|
||||
|
||||
object CglibUtil {
|
||||
fun getFactoryData(clazz: Class<*>): Any? {
|
||||
@ -37,4 +40,4 @@ object CglibUtil {
|
||||
"CGLIB\$SET_STATIC_CALLBACKS"
|
||||
)!!(callbacks)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.tursom.proxy.util
|
||||
|
||||
import cn.tursom.core.uncheckedCast
|
||||
|
||||
class IntMap<V> {
|
||||
companion object {
|
||||
private fun upToPowerOf2(n: Int): Int {
|
||||
if (n and n - 1 == 0) {
|
||||
return n
|
||||
}
|
||||
|
||||
var n = n
|
||||
n = n or (n ushr 1)
|
||||
n = n or (n ushr 2)
|
||||
n = n or (n ushr 4)
|
||||
n = n or (n ushr 8)
|
||||
n = n or (n ushr 16)
|
||||
|
||||
return n + 1
|
||||
}
|
||||
}
|
||||
|
||||
private var array = arrayOfNulls<Any>(32)
|
||||
|
||||
fun clear() {
|
||||
array = arrayOfNulls(16)
|
||||
}
|
||||
|
||||
operator fun get(index: Int): V? = if (index >= array.size) {
|
||||
null
|
||||
} else {
|
||||
array[index].uncheckedCast()
|
||||
}
|
||||
|
||||
operator fun set(index: Int, value: V) {
|
||||
if (index >= array.size) {
|
||||
array = array.copyOf(upToPowerOf2(index + 1))
|
||||
}
|
||||
|
||||
array[index] = value
|
||||
}
|
||||
}
|
@ -1,57 +1,86 @@
|
||||
package cn.tursom.proxy
|
||||
|
||||
import cn.tursom.core.allFieldsSequence
|
||||
import cn.tursom.core.static
|
||||
import com.esotericsoftware.reflectasm.MethodAccess
|
||||
import net.sf.cglib.proxy.InvocationHandler
|
||||
import net.sf.cglib.proxy.MethodProxy
|
||||
import org.junit.Test
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import java.io.File
|
||||
|
||||
class Example {
|
||||
companion object{
|
||||
var bytes: ByteArray? = null
|
||||
|
||||
fun saveBytes(b: ByteArray) {
|
||||
bytes = b
|
||||
}
|
||||
}
|
||||
|
||||
open class TestClass protected constructor() {
|
||||
//@get:ForEachProxy
|
||||
open var a: Int = 0
|
||||
}
|
||||
|
||||
class GetA(
|
||||
private val t: TestClass,
|
||||
) : ProxyMethod {
|
||||
fun getA(): Int {
|
||||
Proxy.callSuper.set(true)
|
||||
return t.a + 1
|
||||
}
|
||||
t: TestClass,
|
||||
) {
|
||||
val t: TestClass = Proxy.getSuperCaller(t)
|
||||
|
||||
val a: Int
|
||||
get() = t.a + 1
|
||||
|
||||
//fun getA(): Int {
|
||||
// return t.a + 1
|
||||
//}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getClass() {
|
||||
val access = MethodAccess.get(TestClass::class.java)
|
||||
println(access)
|
||||
|
||||
val writer = ClassWriter(0)
|
||||
val enhancer = Proxy.newEnhancer(TestClass::class.java)
|
||||
|
||||
enhancer.setCallbackType(InvocationHandler::class.java)
|
||||
enhancer.setCallbackFilter { 0 }
|
||||
|
||||
val clazz = enhancer.createClass()
|
||||
CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor()))
|
||||
val instance = clazz.newInstance()
|
||||
//CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor()))
|
||||
//val instance = clazz.newInstance()
|
||||
enhancer.generateClass(writer)
|
||||
File("TestClass.class").writeBytes(writer.toByteArray())
|
||||
File("TestClass_InvocationHandler.class").writeBytes(writer.toByteArray())
|
||||
clazz.allFieldsSequence.forEach {
|
||||
if (!it.static) return@forEach
|
||||
|
||||
it.isAccessible = true
|
||||
println("${it.name} = ${it[null]}")
|
||||
|
||||
if (it.type == MethodProxy::class.java) {
|
||||
val methodProxy = it[null] as MethodProxy
|
||||
println(methodProxy.signature)
|
||||
println(methodProxy.superName)
|
||||
println(methodProxy.superIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getMethodAccessClass() {
|
||||
val (t, container) = Proxy.get<TestClass>()
|
||||
|
||||
MethodAccess.get(t.javaClass)!!
|
||||
//File("TestClass_MethodAccess.class").writeBytes(bytes!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
//val enhancer = Enhancer()
|
||||
//enhancer.setSuperclass(TestClass::class.java)
|
||||
//enhancer.setCallback(ProxyDispatcher)
|
||||
//val t = enhancer.create() as TestClass
|
||||
//val enhancer = Enhancer()
|
||||
//enhancer.setSuperclass(TestClass::class.java)
|
||||
//enhancer.setCallback(ProxyRefDispatcher {
|
||||
// println("on Dispatcher")
|
||||
// ProxyInterceptor()
|
||||
//})
|
||||
//
|
||||
//val t = enhancer.create() as TestClass
|
||||
//val writer = ClassWriter(0)
|
||||
//enhancer.generateClass(writer)
|
||||
//File("TestClass_ProxyRefDispatcher.class").writeBytes(writer.toByteArray())
|
||||
val (t, container) = Proxy.get<TestClass>()
|
||||
//val t = Proxy.getCachedTarget(TestClass::class.java).newInstance()
|
||||
//val container = Proxy.injectCallback(t)
|
||||
//container as MutableProxyContainer
|
||||
container.addProxy(GetA(t))
|
||||
val getA = GetA(t)
|
||||
println(getA.t == t)
|
||||
|
||||
container.addProxy(getA)
|
||||
|
||||
println(t.javaClass)
|
||||
repeat(10) {
|
||||
|
Loading…
Reference in New Issue
Block a user